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 com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 21 22 import android.animation.ObjectAnimator; 23 import android.animation.ValueAnimator; 24 import android.annotation.NonNull; 25 import android.app.Service; 26 import android.content.Context; 27 import android.graphics.Canvas; 28 import android.graphics.Color; 29 import android.graphics.Matrix; 30 import android.graphics.Paint; 31 import android.graphics.Path; 32 import android.graphics.PixelFormat; 33 import android.graphics.Point; 34 import android.graphics.PorterDuff.Mode; 35 import android.graphics.Rect; 36 import android.graphics.RectF; 37 import android.graphics.Region; 38 import android.os.Handler; 39 import android.os.IBinder; 40 import android.os.Looper; 41 import android.os.Message; 42 import android.text.TextUtils; 43 import android.util.ArraySet; 44 import android.util.Log; 45 import android.util.Slog; 46 import android.util.SparseArray; 47 import android.util.TypedValue; 48 import android.view.MagnificationSpec; 49 import android.view.Surface; 50 import android.view.Surface.OutOfResourcesException; 51 import android.view.SurfaceControl; 52 import android.view.ViewConfiguration; 53 import android.view.WindowInfo; 54 import android.view.WindowManager; 55 import android.view.WindowManagerInternal.MagnificationCallbacks; 56 import android.view.WindowManagerInternal.WindowsForAccessibilityCallback; 57 import android.view.WindowManagerPolicy; 58 import android.view.animation.DecelerateInterpolator; 59 import android.view.animation.Interpolator; 60 61 import com.android.internal.R; 62 import com.android.internal.os.SomeArgs; 63 64 import java.util.ArrayList; 65 import java.util.HashSet; 66 import java.util.List; 67 import java.util.Set; 68 69 /** 70 * This class contains the accessibility related logic of the window manger. 71 */ 72 final class AccessibilityController { 73 74 private final WindowManagerService mWindowManagerService; 75 76 private static final float[] sTempFloats = new float[9]; 77 AccessibilityController(WindowManagerService service)78 public AccessibilityController(WindowManagerService service) { 79 mWindowManagerService = service; 80 } 81 82 private DisplayMagnifier mDisplayMagnifier; 83 84 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver; 85 setMagnificationCallbacksLocked(MagnificationCallbacks callbacks)86 public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) { 87 if (callbacks != null) { 88 if (mDisplayMagnifier != null) { 89 throw new IllegalStateException("Magnification callbacks already set!"); 90 } 91 mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks); 92 } else { 93 if (mDisplayMagnifier == null) { 94 throw new IllegalStateException("Magnification callbacks already cleared!"); 95 } 96 mDisplayMagnifier.destroyLocked(); 97 mDisplayMagnifier = null; 98 } 99 } 100 setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback)101 public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) { 102 if (callback != null) { 103 if (mWindowsForAccessibilityObserver != null) { 104 throw new IllegalStateException( 105 "Windows for accessibility callback already set!"); 106 } 107 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver( 108 mWindowManagerService, callback); 109 } else { 110 if (mWindowsForAccessibilityObserver == null) { 111 throw new IllegalStateException( 112 "Windows for accessibility callback already cleared!"); 113 } 114 mWindowsForAccessibilityObserver = null; 115 } 116 } 117 performComputeChangedWindowsNotLocked()118 public void performComputeChangedWindowsNotLocked() { 119 WindowsForAccessibilityObserver observer = null; 120 synchronized (mWindowManagerService) { 121 observer = mWindowsForAccessibilityObserver; 122 } 123 if (observer != null) { 124 observer.performComputeChangedWindowsNotLocked(); 125 } 126 } 127 setMagnificationSpecLocked(MagnificationSpec spec)128 public void setMagnificationSpecLocked(MagnificationSpec spec) { 129 if (mDisplayMagnifier != null) { 130 mDisplayMagnifier.setMagnificationSpecLocked(spec); 131 } 132 if (mWindowsForAccessibilityObserver != null) { 133 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 134 } 135 } 136 getMagnificationRegionLocked(Region outMagnificationRegion)137 public void getMagnificationRegionLocked(Region outMagnificationRegion) { 138 if (mDisplayMagnifier != null) { 139 mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion); 140 } 141 } 142 onRectangleOnScreenRequestedLocked(Rect rectangle)143 public void onRectangleOnScreenRequestedLocked(Rect rectangle) { 144 if (mDisplayMagnifier != null) { 145 mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle); 146 } 147 // Not relevant for the window observer. 148 } 149 onWindowLayersChangedLocked()150 public void onWindowLayersChangedLocked() { 151 if (mDisplayMagnifier != null) { 152 mDisplayMagnifier.onWindowLayersChangedLocked(); 153 } 154 if (mWindowsForAccessibilityObserver != null) { 155 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 156 } 157 } 158 onRotationChangedLocked(DisplayContent displayContent)159 public void onRotationChangedLocked(DisplayContent displayContent) { 160 if (mDisplayMagnifier != null) { 161 mDisplayMagnifier.onRotationChangedLocked(displayContent); 162 } 163 if (mWindowsForAccessibilityObserver != null) { 164 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 165 } 166 } 167 onAppWindowTransitionLocked(WindowState windowState, int transition)168 public void onAppWindowTransitionLocked(WindowState windowState, int transition) { 169 if (mDisplayMagnifier != null) { 170 mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition); 171 } 172 // Not relevant for the window observer. 173 } 174 onWindowTransitionLocked(WindowState windowState, int transition)175 public void onWindowTransitionLocked(WindowState windowState, int transition) { 176 if (mDisplayMagnifier != null) { 177 mDisplayMagnifier.onWindowTransitionLocked(windowState, transition); 178 } 179 if (mWindowsForAccessibilityObserver != null) { 180 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 181 } 182 } 183 onWindowFocusChangedNotLocked()184 public void onWindowFocusChangedNotLocked() { 185 // Not relevant for the display magnifier. 186 187 WindowsForAccessibilityObserver observer = null; 188 synchronized (mWindowManagerService) { 189 observer = mWindowsForAccessibilityObserver; 190 } 191 if (observer != null) { 192 observer.performComputeChangedWindowsNotLocked(); 193 } 194 } 195 196 onSomeWindowResizedOrMovedLocked()197 public void onSomeWindowResizedOrMovedLocked() { 198 // Not relevant for the display magnifier. 199 200 if (mWindowsForAccessibilityObserver != null) { 201 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked(); 202 } 203 } 204 205 /** NOTE: This has to be called within a surface transaction. */ drawMagnifiedRegionBorderIfNeededLocked()206 public void drawMagnifiedRegionBorderIfNeededLocked() { 207 if (mDisplayMagnifier != null) { 208 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(); 209 } 210 // Not relevant for the window observer. 211 } 212 getMagnificationSpecForWindowLocked(WindowState windowState)213 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { 214 if (mDisplayMagnifier != null) { 215 return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState); 216 } 217 return null; 218 } 219 hasCallbacksLocked()220 public boolean hasCallbacksLocked() { 221 return (mDisplayMagnifier != null 222 || mWindowsForAccessibilityObserver != null); 223 } 224 setForceShowMagnifiableBoundsLocked(boolean show)225 public void setForceShowMagnifiableBoundsLocked(boolean show) { 226 if (mDisplayMagnifier != null) { 227 mDisplayMagnifier.setForceShowMagnifiableBoundsLocked(show); 228 mDisplayMagnifier.showMagnificationBoundsIfNeeded(); 229 } 230 } 231 populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix)232 private static void populateTransformationMatrixLocked(WindowState windowState, 233 Matrix outMatrix) { 234 sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx; 235 sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx; 236 sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy; 237 sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy; 238 sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x; 239 sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y; 240 sTempFloats[Matrix.MPERSP_0] = 0; 241 sTempFloats[Matrix.MPERSP_1] = 0; 242 sTempFloats[Matrix.MPERSP_2] = 1; 243 outMatrix.setValues(sTempFloats); 244 } 245 246 /** 247 * This class encapsulates the functionality related to display magnification. 248 */ 249 private static final class DisplayMagnifier { 250 251 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM; 252 253 private static final boolean DEBUG_WINDOW_TRANSITIONS = false; 254 private static final boolean DEBUG_ROTATION = false; 255 private static final boolean DEBUG_LAYERS = false; 256 private static final boolean DEBUG_RECTANGLE_REQUESTED = false; 257 private static final boolean DEBUG_VIEWPORT_WINDOW = false; 258 259 private final Rect mTempRect1 = new Rect(); 260 private final Rect mTempRect2 = new Rect(); 261 262 private final Region mTempRegion1 = new Region(); 263 private final Region mTempRegion2 = new Region(); 264 private final Region mTempRegion3 = new Region(); 265 private final Region mTempRegion4 = new Region(); 266 267 private final Context mContext; 268 private final WindowManagerService mWindowManagerService; 269 private final MagnifiedViewport mMagnifedViewport; 270 private final Handler mHandler; 271 272 private final MagnificationCallbacks mCallbacks; 273 274 private final long mLongAnimationDuration; 275 276 private boolean mForceShowMagnifiableBounds = false; 277 DisplayMagnifier(WindowManagerService windowManagerService, MagnificationCallbacks callbacks)278 public DisplayMagnifier(WindowManagerService windowManagerService, 279 MagnificationCallbacks callbacks) { 280 mContext = windowManagerService.mContext; 281 mWindowManagerService = windowManagerService; 282 mCallbacks = callbacks; 283 mHandler = new MyHandler(mWindowManagerService.mH.getLooper()); 284 mMagnifedViewport = new MagnifiedViewport(); 285 mLongAnimationDuration = mContext.getResources().getInteger( 286 com.android.internal.R.integer.config_longAnimTime); 287 } 288 setMagnificationSpecLocked(MagnificationSpec spec)289 public void setMagnificationSpecLocked(MagnificationSpec spec) { 290 mMagnifedViewport.updateMagnificationSpecLocked(spec); 291 mMagnifedViewport.recomputeBoundsLocked(); 292 mWindowManagerService.scheduleAnimationLocked(); 293 } 294 setForceShowMagnifiableBoundsLocked(boolean show)295 public void setForceShowMagnifiableBoundsLocked(boolean show) { 296 mForceShowMagnifiableBounds = show; 297 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true); 298 } 299 isForceShowingMagnifiableBoundsLocked()300 public boolean isForceShowingMagnifiableBoundsLocked() { 301 return mForceShowMagnifiableBounds; 302 } 303 onRectangleOnScreenRequestedLocked(Rect rectangle)304 public void onRectangleOnScreenRequestedLocked(Rect rectangle) { 305 if (DEBUG_RECTANGLE_REQUESTED) { 306 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); 307 } 308 if (!mMagnifedViewport.isMagnifyingLocked()) { 309 return; 310 } 311 Rect magnifiedRegionBounds = mTempRect2; 312 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds); 313 if (magnifiedRegionBounds.contains(rectangle)) { 314 return; 315 } 316 SomeArgs args = SomeArgs.obtain(); 317 args.argi1 = rectangle.left; 318 args.argi2 = rectangle.top; 319 args.argi3 = rectangle.right; 320 args.argi4 = rectangle.bottom; 321 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, 322 args).sendToTarget(); 323 } 324 onWindowLayersChangedLocked()325 public void onWindowLayersChangedLocked() { 326 if (DEBUG_LAYERS) { 327 Slog.i(LOG_TAG, "Layers changed."); 328 } 329 mMagnifedViewport.recomputeBoundsLocked(); 330 mWindowManagerService.scheduleAnimationLocked(); 331 } 332 onRotationChangedLocked(DisplayContent displayContent)333 public void onRotationChangedLocked(DisplayContent displayContent) { 334 if (DEBUG_ROTATION) { 335 final int rotation = displayContent.getRotation(); 336 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation) 337 + " displayId: " + displayContent.getDisplayId()); 338 } 339 mMagnifedViewport.onRotationChangedLocked(); 340 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); 341 } 342 onAppWindowTransitionLocked(WindowState windowState, int transition)343 public void onAppWindowTransitionLocked(WindowState windowState, int transition) { 344 if (DEBUG_WINDOW_TRANSITIONS) { 345 Slog.i(LOG_TAG, "Window transition: " 346 + AppTransition.appTransitionToString(transition) 347 + " displayId: " + windowState.getDisplayId()); 348 } 349 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); 350 if (magnifying) { 351 switch (transition) { 352 case AppTransition.TRANSIT_ACTIVITY_OPEN: 353 case AppTransition.TRANSIT_TASK_OPEN: 354 case AppTransition.TRANSIT_TASK_TO_FRONT: 355 case AppTransition.TRANSIT_WALLPAPER_OPEN: 356 case AppTransition.TRANSIT_WALLPAPER_CLOSE: 357 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: { 358 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED); 359 } 360 } 361 } 362 } 363 onWindowTransitionLocked(WindowState windowState, int transition)364 public void onWindowTransitionLocked(WindowState windowState, int transition) { 365 if (DEBUG_WINDOW_TRANSITIONS) { 366 Slog.i(LOG_TAG, "Window transition: " 367 + AppTransition.appTransitionToString(transition) 368 + " displayId: " + windowState.getDisplayId()); 369 } 370 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); 371 final int type = windowState.mAttrs.type; 372 switch (transition) { 373 case WindowManagerPolicy.TRANSIT_ENTER: 374 case WindowManagerPolicy.TRANSIT_SHOW: { 375 if (!magnifying) { 376 break; 377 } 378 switch (type) { 379 case WindowManager.LayoutParams.TYPE_APPLICATION: 380 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: 381 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: 382 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: 383 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: 384 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL: 385 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: 386 case WindowManager.LayoutParams.TYPE_SEARCH_BAR: 387 case WindowManager.LayoutParams.TYPE_PHONE: 388 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: 389 case WindowManager.LayoutParams.TYPE_TOAST: 390 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: 391 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: 392 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: 393 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: 394 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: 395 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: 396 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: 397 case WindowManager.LayoutParams.TYPE_QS_DIALOG: 398 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: { 399 Rect magnifiedRegionBounds = mTempRect2; 400 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked( 401 magnifiedRegionBounds); 402 Rect touchableRegionBounds = mTempRect1; 403 windowState.getTouchableRegion(mTempRegion1); 404 mTempRegion1.getBounds(touchableRegionBounds); 405 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) { 406 mCallbacks.onRectangleOnScreenRequested( 407 touchableRegionBounds.left, 408 touchableRegionBounds.top, 409 touchableRegionBounds.right, 410 touchableRegionBounds.bottom); 411 } 412 } break; 413 } break; 414 } 415 } 416 } 417 getMagnificationSpecForWindowLocked(WindowState windowState)418 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { 419 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked(); 420 if (spec != null && !spec.isNop()) { 421 WindowManagerPolicy policy = mWindowManagerService.mPolicy; 422 final int windowType = windowState.mAttrs.type; 423 if (!policy.isTopLevelWindow(windowType) && windowState.isChildWindow() 424 && !policy.canMagnifyWindow(windowType)) { 425 return null; 426 } 427 if (!policy.canMagnifyWindow(windowState.mAttrs.type)) { 428 return null; 429 } 430 } 431 return spec; 432 } 433 getMagnificationRegionLocked(Region outMagnificationRegion)434 public void getMagnificationRegionLocked(Region outMagnificationRegion) { 435 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion); 436 } 437 destroyLocked()438 public void destroyLocked() { 439 mMagnifedViewport.destroyWindow(); 440 } 441 442 // Can be called outside of a surface transaction showMagnificationBoundsIfNeeded()443 public void showMagnificationBoundsIfNeeded() { 444 mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED) 445 .sendToTarget(); 446 } 447 448 /** NOTE: This has to be called within a surface transaction. */ drawMagnifiedRegionBorderIfNeededLocked()449 public void drawMagnifiedRegionBorderIfNeededLocked() { 450 mMagnifedViewport.drawWindowIfNeededLocked(); 451 } 452 453 private final class MagnifiedViewport { 454 455 private final SparseArray<WindowState> mTempWindowStates = 456 new SparseArray<WindowState>(); 457 458 private final RectF mTempRectF = new RectF(); 459 460 private final Point mTempPoint = new Point(); 461 462 private final Matrix mTempMatrix = new Matrix(); 463 464 private final Region mMagnificationRegion = new Region(); 465 private final Region mOldMagnificationRegion = new Region(); 466 467 private final Path mCircularPath; 468 469 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain(); 470 471 private final WindowManager mWindowManager; 472 473 private final float mBorderWidth; 474 private final int mHalfBorderWidth; 475 private final int mDrawBorderInset; 476 477 private final ViewportWindow mWindow; 478 479 private boolean mFullRedrawNeeded; 480 MagnifiedViewport()481 public MagnifiedViewport() { 482 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE); 483 mBorderWidth = mContext.getResources().getDimension( 484 com.android.internal.R.dimen.accessibility_magnification_indicator_width); 485 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2); 486 mDrawBorderInset = (int) mBorderWidth / 2; 487 mWindow = new ViewportWindow(mContext); 488 489 if (mContext.getResources().getConfiguration().isScreenRound()) { 490 mCircularPath = new Path(); 491 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 492 final int centerXY = mTempPoint.x / 2; 493 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW); 494 } else { 495 mCircularPath = null; 496 } 497 498 recomputeBoundsLocked(); 499 } 500 getMagnificationRegionLocked(@onNull Region outMagnificationRegion)501 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) { 502 outMagnificationRegion.set(mMagnificationRegion); 503 } 504 updateMagnificationSpecLocked(MagnificationSpec spec)505 public void updateMagnificationSpecLocked(MagnificationSpec spec) { 506 if (spec != null) { 507 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY); 508 } else { 509 mMagnificationSpec.clear(); 510 } 511 // If this message is pending we are in a rotation animation and do not want 512 // to show the border. We will do so when the pending message is handled. 513 if (!mHandler.hasMessages( 514 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { 515 setMagnifiedRegionBorderShownLocked( 516 isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true); 517 } 518 } 519 recomputeBoundsLocked()520 public void recomputeBoundsLocked() { 521 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint); 522 final int screenWidth = mTempPoint.x; 523 final int screenHeight = mTempPoint.y; 524 525 mMagnificationRegion.set(0, 0, 0, 0); 526 final Region availableBounds = mTempRegion1; 527 availableBounds.set(0, 0, screenWidth, screenHeight); 528 529 if (mCircularPath != null) { 530 availableBounds.setPath(mCircularPath, availableBounds); 531 } 532 533 Region nonMagnifiedBounds = mTempRegion4; 534 nonMagnifiedBounds.set(0, 0, 0, 0); 535 536 SparseArray<WindowState> visibleWindows = mTempWindowStates; 537 visibleWindows.clear(); 538 populateWindowsOnScreenLocked(visibleWindows); 539 540 final int visibleWindowCount = visibleWindows.size(); 541 for (int i = visibleWindowCount - 1; i >= 0; i--) { 542 WindowState windowState = visibleWindows.valueAt(i); 543 if (windowState.mAttrs.type == WindowManager 544 .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) { 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 onRotationChangedLocked()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 setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate)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 getMagnifiedFrameInContentCoordsLocked(Rect rect)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 isMagnifyingLocked()659 public boolean isMagnifyingLocked() { 660 return mMagnificationSpec.scale > 1.0f; 661 } 662 getMagnificationSpecLocked()663 public MagnificationSpec getMagnificationSpecLocked() { 664 return mMagnificationSpec; 665 } 666 667 /** NOTE: This has to be called within a surface transaction. */ drawWindowIfNeededLocked()668 public void drawWindowIfNeededLocked() { 669 recomputeBoundsLocked(); 670 mWindow.drawIfNeeded(); 671 } 672 destroyWindow()673 public void destroyWindow() { 674 mWindow.releaseSurface(); 675 } 676 populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows)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 ViewportWindow(Context context)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 WindowManager.LayoutParams.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 setShown(boolean shown, boolean animate)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. getAlpha()753 public int getAlpha() { 754 synchronized (mWindowManagerService.mWindowMap) { 755 return mAlpha; 756 } 757 } 758 setAlpha(int alpha)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 setBounds(Region bounds)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 updateSize()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 invalidate(Rect dirtyRect)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. */ drawIfNeeded()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 releaseSurface()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 AnimationController(Context context, Looper looper)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 onFrameShownStateChanged(boolean shown, boolean animate)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 handleMessage(Message message)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 MyHandler(Looper looper)919 public MyHandler(Looper looper) { 920 super(looper); 921 } 922 923 @Override handleMessage(Message message)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 WindowsForAccessibilityObserver(WindowManagerService windowManagerService, WindowsForAccessibilityCallback callback)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 performComputeChangedWindowsNotLocked()1017 public void performComputeChangedWindowsNotLocked() { 1018 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS); 1019 computeChangedWindows(); 1020 } 1021 scheduleComputeChangedWindowsLocked()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 computeChangedWindows()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 computeWindowBoundsInScreen(WindowState windowState, Rect outBounds)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 obtainPopulatedWindowInfo( WindowState windowState, Rect boundsInScreen)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 cacheWindows(List<WindowInfo> windows)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 windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow)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 clearAndRecycleWindows(List<WindowInfo> windows)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 isReportedWindowType(int windowType)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 != WindowManager.LayoutParams.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 populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows)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 MyHandler(Looper looper)1334 public MyHandler(Looper looper) { 1335 super(looper, null, false); 1336 } 1337 1338 @Override 1339 @SuppressWarnings("unchecked") handleMessage(Message message)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