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