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