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