1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR; 20 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY; 21 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER; 22 import static android.view.WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY; 23 24 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME; 25 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 26 import static com.android.server.wm.utils.RegionUtils.forEachRect; 27 28 import android.animation.ObjectAnimator; 29 import android.animation.ValueAnimator; 30 import android.annotation.NonNull; 31 import android.content.Context; 32 import android.graphics.Canvas; 33 import android.graphics.Color; 34 import android.graphics.Matrix; 35 import android.graphics.Paint; 36 import android.graphics.Path; 37 import android.graphics.PixelFormat; 38 import android.graphics.Point; 39 import android.graphics.PorterDuff.Mode; 40 import android.graphics.Rect; 41 import android.graphics.RectF; 42 import android.graphics.Region; 43 import android.os.Handler; 44 import android.os.IBinder; 45 import android.os.Looper; 46 import android.os.Message; 47 import android.util.ArraySet; 48 import android.util.IntArray; 49 import android.util.Slog; 50 import android.util.SparseArray; 51 import android.util.TypedValue; 52 import android.view.Display; 53 import android.view.InsetsSource; 54 import android.view.MagnificationSpec; 55 import android.view.Surface; 56 import android.view.Surface.OutOfResourcesException; 57 import android.view.SurfaceControl; 58 import android.view.ViewConfiguration; 59 import android.view.WindowInfo; 60 import android.view.WindowManager; 61 import android.view.animation.DecelerateInterpolator; 62 import android.view.animation.Interpolator; 63 64 import com.android.internal.R; 65 import com.android.internal.os.SomeArgs; 66 import com.android.server.policy.WindowManagerPolicy; 67 import com.android.server.wm.WindowManagerInternal.MagnificationCallbacks; 68 import com.android.server.wm.WindowManagerInternal.WindowsForAccessibilityCallback; 69 70 import java.io.PrintWriter; 71 import java.util.ArrayList; 72 import java.util.HashSet; 73 import java.util.List; 74 import java.util.Set; 75 76 /** 77 * This class contains the accessibility related logic of the window manager. 78 */ 79 final class AccessibilityController { 80 81 private final WindowManagerService mService; 82 83 private static final Rect EMPTY_RECT = new Rect(); 84 private static final float[] sTempFloats = new float[9]; 85 AccessibilityController(WindowManagerService service)86 public AccessibilityController(WindowManagerService service) { 87 mService = service; 88 } 89 90 private SparseArray<DisplayMagnifier> mDisplayMagnifiers = new SparseArray<>(); 91 92 private SparseArray<WindowsForAccessibilityObserver> mWindowsForAccessibilityObserver = 93 new SparseArray<>(); 94 setMagnificationCallbacksLocked(int displayId, MagnificationCallbacks callbacks)95 public boolean setMagnificationCallbacksLocked(int displayId, 96 MagnificationCallbacks callbacks) { 97 boolean result = false; 98 if (callbacks != null) { 99 if (mDisplayMagnifiers.get(displayId) != null) { 100 throw new IllegalStateException("Magnification callbacks already set!"); 101 } 102 final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); 103 if (dc != null) { 104 final Display display = dc.getDisplay(); 105 if (display != null && display.getType() != Display.TYPE_OVERLAY) { 106 mDisplayMagnifiers.put(displayId, new DisplayMagnifier( 107 mService, dc, display, callbacks)); 108 result = true; 109 } 110 } 111 } else { 112 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 113 if (displayMagnifier == null) { 114 throw new IllegalStateException("Magnification callbacks already cleared!"); 115 } 116 displayMagnifier.destroyLocked(); 117 mDisplayMagnifiers.remove(displayId); 118 result = true; 119 } 120 return result; 121 } 122 123 /** 124 * Sets a callback for observing which windows are touchable for the purposes 125 * of accessibility on specified display. 126 * 127 * @param displayId The logical display id. 128 * @param callback The callback. 129 * @return {@code false} if display id is not valid or an embedded display. 130 */ setWindowsForAccessibilityCallbackLocked(int displayId, WindowsForAccessibilityCallback callback)131 public boolean setWindowsForAccessibilityCallbackLocked(int displayId, 132 WindowsForAccessibilityCallback callback) { 133 final DisplayContent dc = mService.mRoot.getDisplayContentOrCreate(displayId); 134 if (dc == null) { 135 return false; 136 } 137 138 if (callback != null) { 139 if (isEmbeddedDisplay(dc)) { 140 // If this display is an embedded one, its window observer should have been set from 141 // window manager after setting its parent window. But if its window observer is 142 // empty, that means this mapping didn't be set, and needs to do this again. 143 // This happened when accessibility window observer is disabled and enabled again. 144 if (mWindowsForAccessibilityObserver.get(displayId) == null) { 145 handleWindowObserverOfEmbeddedDisplayLocked(displayId, dc.getParentWindow()); 146 } 147 return false; 148 } else if (mWindowsForAccessibilityObserver.get(displayId) != null) { 149 throw new IllegalStateException( 150 "Windows for accessibility callback of display " 151 + displayId + " already set!"); 152 } 153 mWindowsForAccessibilityObserver.put(displayId, 154 new WindowsForAccessibilityObserver(mService, displayId, callback)); 155 } else { 156 if (isEmbeddedDisplay(dc)) { 157 // If this display is an embedded one, its window observer should be removed along 158 // with the window observer of its parent display removed because the window 159 // observer of the embedded display and its parent display is the same, and would 160 // be removed together when stopping the window tracking of its parent display. So 161 // here don't need to do removing window observer of the embedded display again. 162 return true; 163 } 164 final WindowsForAccessibilityObserver windowsForA11yObserver = 165 mWindowsForAccessibilityObserver.get(displayId); 166 if (windowsForA11yObserver == null) { 167 throw new IllegalStateException( 168 "Windows for accessibility callback of display " + displayId 169 + " already cleared!"); 170 } 171 removeObserverOfEmbeddedDisplay(windowsForA11yObserver); 172 mWindowsForAccessibilityObserver.remove(displayId); 173 } 174 return true; 175 } 176 performComputeChangedWindowsNotLocked(int displayId, boolean forceSend)177 public void performComputeChangedWindowsNotLocked(int displayId, boolean forceSend) { 178 WindowsForAccessibilityObserver observer = null; 179 synchronized (mService) { 180 final WindowsForAccessibilityObserver windowsForA11yObserver = 181 mWindowsForAccessibilityObserver.get(displayId); 182 if (windowsForA11yObserver != null) { 183 observer = windowsForA11yObserver; 184 } 185 } 186 if (observer != null) { 187 observer.performComputeChangedWindowsNotLocked(forceSend); 188 } 189 } 190 setMagnificationSpecLocked(int displayId, MagnificationSpec spec)191 public void setMagnificationSpecLocked(int displayId, MagnificationSpec spec) { 192 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 193 if (displayMagnifier != null) { 194 displayMagnifier.setMagnificationSpecLocked(spec); 195 } 196 final WindowsForAccessibilityObserver windowsForA11yObserver = 197 mWindowsForAccessibilityObserver.get(displayId); 198 if (windowsForA11yObserver != null) { 199 windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); 200 } 201 } 202 getMagnificationRegionLocked(int displayId, Region outMagnificationRegion)203 public void getMagnificationRegionLocked(int displayId, Region outMagnificationRegion) { 204 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 205 if (displayMagnifier != null) { 206 displayMagnifier.getMagnificationRegionLocked(outMagnificationRegion); 207 } 208 } 209 onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle)210 public void onRectangleOnScreenRequestedLocked(int displayId, Rect rectangle) { 211 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 212 if (displayMagnifier != null) { 213 displayMagnifier.onRectangleOnScreenRequestedLocked(rectangle); 214 } 215 // Not relevant for the window observer. 216 } 217 onWindowLayersChangedLocked(int displayId)218 public void onWindowLayersChangedLocked(int displayId) { 219 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 220 if (displayMagnifier != null) { 221 displayMagnifier.onWindowLayersChangedLocked(); 222 } 223 final WindowsForAccessibilityObserver windowsForA11yObserver = 224 mWindowsForAccessibilityObserver.get(displayId); 225 if (windowsForA11yObserver != null) { 226 windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); 227 } 228 } 229 onRotationChangedLocked(DisplayContent displayContent)230 public void onRotationChangedLocked(DisplayContent displayContent) { 231 final int displayId = displayContent.getDisplayId(); 232 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 233 if (displayMagnifier != null) { 234 displayMagnifier.onRotationChangedLocked(displayContent); 235 } 236 final WindowsForAccessibilityObserver windowsForA11yObserver = 237 mWindowsForAccessibilityObserver.get(displayId); 238 if (windowsForA11yObserver != null) { 239 windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); 240 } 241 } 242 onAppWindowTransitionLocked(int displayId, int transition)243 public void onAppWindowTransitionLocked(int displayId, int transition) { 244 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 245 if (displayMagnifier != null) { 246 displayMagnifier.onAppWindowTransitionLocked(displayId, transition); 247 } 248 // Not relevant for the window observer. 249 } 250 onWindowTransitionLocked(WindowState windowState, int transition)251 public void onWindowTransitionLocked(WindowState windowState, int transition) { 252 final int displayId = windowState.getDisplayId(); 253 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 254 if (displayMagnifier != null) { 255 displayMagnifier.onWindowTransitionLocked(windowState, transition); 256 } 257 final WindowsForAccessibilityObserver windowsForA11yObserver = 258 mWindowsForAccessibilityObserver.get(displayId); 259 if (windowsForA11yObserver != null) { 260 windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); 261 } 262 } 263 onWindowFocusChangedNotLocked(int displayId)264 public void onWindowFocusChangedNotLocked(int displayId) { 265 // Not relevant for the display magnifier. 266 267 WindowsForAccessibilityObserver observer = null; 268 synchronized (mService) { 269 final WindowsForAccessibilityObserver windowsForA11yObserver = 270 mWindowsForAccessibilityObserver.get(displayId); 271 if (windowsForA11yObserver != null) { 272 observer = windowsForA11yObserver; 273 } 274 } 275 if (observer != null) { 276 observer.performComputeChangedWindowsNotLocked(false); 277 } 278 } 279 280 /** 281 * Called when the location or the size of the window is changed. Moving the window to 282 * another display is also taken into consideration. 283 * @param displayIds the display ids of displays when the situation happens. 284 */ onSomeWindowResizedOrMovedLocked(int... displayIds)285 public void onSomeWindowResizedOrMovedLocked(int... displayIds) { 286 // Not relevant for the display magnifier. 287 for (int i = 0; i < displayIds.length; i++) { 288 final WindowsForAccessibilityObserver windowsForA11yObserver = 289 mWindowsForAccessibilityObserver.get(displayIds[i]); 290 if (windowsForA11yObserver != null) { 291 windowsForA11yObserver.scheduleComputeChangedWindowsLocked(); 292 } 293 } 294 } 295 drawMagnifiedRegionBorderIfNeededLocked(int displayId, SurfaceControl.Transaction t)296 public void drawMagnifiedRegionBorderIfNeededLocked(int displayId, 297 SurfaceControl.Transaction t) { 298 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 299 if (displayMagnifier != null) { 300 displayMagnifier.drawMagnifiedRegionBorderIfNeededLocked(t); 301 } 302 // Not relevant for the window observer. 303 } 304 getMagnificationSpecForWindowLocked(WindowState windowState)305 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { 306 final int displayId = windowState.getDisplayId(); 307 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 308 if (displayMagnifier != null) { 309 return displayMagnifier.getMagnificationSpecForWindowLocked(windowState); 310 } 311 return null; 312 } 313 hasCallbacksLocked()314 public boolean hasCallbacksLocked() { 315 return (mDisplayMagnifiers.size() > 0 316 || mWindowsForAccessibilityObserver.size() > 0); 317 } 318 setForceShowMagnifiableBoundsLocked(int displayId, boolean show)319 public void setForceShowMagnifiableBoundsLocked(int displayId, boolean show) { 320 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.get(displayId); 321 if (displayMagnifier != null) { 322 displayMagnifier.setForceShowMagnifiableBoundsLocked(show); 323 displayMagnifier.showMagnificationBoundsIfNeeded(); 324 } 325 } 326 handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId, WindowState parentWindow)327 public void handleWindowObserverOfEmbeddedDisplayLocked(int embeddedDisplayId, 328 WindowState parentWindow) { 329 if (embeddedDisplayId == Display.DEFAULT_DISPLAY || parentWindow == null) { 330 return; 331 } 332 // Finds the parent display of this embedded display 333 final int parentDisplayId; 334 WindowState candidate = parentWindow; 335 while (candidate != null) { 336 parentWindow = candidate; 337 candidate = parentWindow.getDisplayContent().getParentWindow(); 338 } 339 parentDisplayId = parentWindow.getDisplayId(); 340 // Uses the observer of parent display 341 final WindowsForAccessibilityObserver windowsForA11yObserver = 342 mWindowsForAccessibilityObserver.get(parentDisplayId); 343 344 if (windowsForA11yObserver != null) { 345 windowsForA11yObserver.addEmbeddedDisplay(embeddedDisplayId); 346 // Replaces the observer of embedded display to the one of parent display 347 mWindowsForAccessibilityObserver.put(embeddedDisplayId, windowsForA11yObserver); 348 } 349 } 350 populateTransformationMatrixLocked(WindowState windowState, Matrix outMatrix)351 private static void populateTransformationMatrixLocked(WindowState windowState, 352 Matrix outMatrix) { 353 windowState.getTransformationMatrix(sTempFloats, outMatrix); 354 } 355 dump(PrintWriter pw, String prefix)356 void dump(PrintWriter pw, String prefix) { 357 for (int i = 0; i < mDisplayMagnifiers.size(); i++) { 358 final DisplayMagnifier displayMagnifier = mDisplayMagnifiers.valueAt(i); 359 if (displayMagnifier != null) { 360 displayMagnifier.dump(pw, prefix 361 + "Magnification display# " + mDisplayMagnifiers.keyAt(i)); 362 } 363 } 364 } 365 removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver observerOfParentDisplay)366 private void removeObserverOfEmbeddedDisplay(WindowsForAccessibilityObserver 367 observerOfParentDisplay) { 368 final IntArray embeddedDisplayIdList = 369 observerOfParentDisplay.getAndClearEmbeddedDisplayIdList(); 370 371 for (int index = 0; index < embeddedDisplayIdList.size(); index++) { 372 final int embeddedDisplayId = embeddedDisplayIdList.get(index); 373 mWindowsForAccessibilityObserver.remove(embeddedDisplayId); 374 } 375 } 376 isEmbeddedDisplay(DisplayContent dc)377 private static boolean isEmbeddedDisplay(DisplayContent dc) { 378 final Display display = dc.getDisplay(); 379 380 return display.getType() == Display.TYPE_VIRTUAL && dc.getParentWindow() != null; 381 } 382 383 /** 384 * This class encapsulates the functionality related to display magnification. 385 */ 386 private static final class DisplayMagnifier { 387 388 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM; 389 390 private static final boolean DEBUG_WINDOW_TRANSITIONS = false; 391 private static final boolean DEBUG_ROTATION = false; 392 private static final boolean DEBUG_LAYERS = false; 393 private static final boolean DEBUG_RECTANGLE_REQUESTED = false; 394 private static final boolean DEBUG_VIEWPORT_WINDOW = false; 395 396 private final Rect mTempRect1 = new Rect(); 397 private final Rect mTempRect2 = new Rect(); 398 399 private final Region mTempRegion1 = new Region(); 400 private final Region mTempRegion2 = new Region(); 401 private final Region mTempRegion3 = new Region(); 402 private final Region mTempRegion4 = new Region(); 403 404 private final Context mDisplayContext; 405 private final WindowManagerService mService; 406 private final MagnifiedViewport mMagnifedViewport; 407 private final Handler mHandler; 408 private final DisplayContent mDisplayContent; 409 private final Display mDisplay; 410 411 private final MagnificationCallbacks mCallbacks; 412 413 private final long mLongAnimationDuration; 414 415 private boolean mForceShowMagnifiableBounds = false; 416 DisplayMagnifier(WindowManagerService windowManagerService, DisplayContent displayContent, Display display, MagnificationCallbacks callbacks)417 public DisplayMagnifier(WindowManagerService windowManagerService, 418 DisplayContent displayContent, 419 Display display, 420 MagnificationCallbacks callbacks) { 421 mDisplayContext = windowManagerService.mContext.createDisplayContext(display); 422 mService = windowManagerService; 423 mCallbacks = callbacks; 424 mDisplayContent = displayContent; 425 mDisplay = display; 426 mHandler = new MyHandler(mService.mH.getLooper()); 427 mMagnifedViewport = new MagnifiedViewport(); 428 mLongAnimationDuration = mDisplayContext.getResources().getInteger( 429 com.android.internal.R.integer.config_longAnimTime); 430 } 431 setMagnificationSpecLocked(MagnificationSpec spec)432 public void setMagnificationSpecLocked(MagnificationSpec spec) { 433 mMagnifedViewport.updateMagnificationSpecLocked(spec); 434 mMagnifedViewport.recomputeBoundsLocked(); 435 436 mService.applyMagnificationSpecLocked(mDisplay.getDisplayId(), spec); 437 mService.scheduleAnimationLocked(); 438 } 439 setForceShowMagnifiableBoundsLocked(boolean show)440 public void setForceShowMagnifiableBoundsLocked(boolean show) { 441 mForceShowMagnifiableBounds = show; 442 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(show, true); 443 } 444 isForceShowingMagnifiableBoundsLocked()445 public boolean isForceShowingMagnifiableBoundsLocked() { 446 return mForceShowMagnifiableBounds; 447 } 448 onRectangleOnScreenRequestedLocked(Rect rectangle)449 public void onRectangleOnScreenRequestedLocked(Rect rectangle) { 450 if (DEBUG_RECTANGLE_REQUESTED) { 451 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle); 452 } 453 if (!mMagnifedViewport.isMagnifyingLocked()) { 454 return; 455 } 456 Rect magnifiedRegionBounds = mTempRect2; 457 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds); 458 if (magnifiedRegionBounds.contains(rectangle)) { 459 return; 460 } 461 SomeArgs args = SomeArgs.obtain(); 462 args.argi1 = rectangle.left; 463 args.argi2 = rectangle.top; 464 args.argi3 = rectangle.right; 465 args.argi4 = rectangle.bottom; 466 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED, 467 args).sendToTarget(); 468 } 469 onWindowLayersChangedLocked()470 public void onWindowLayersChangedLocked() { 471 if (DEBUG_LAYERS) { 472 Slog.i(LOG_TAG, "Layers changed."); 473 } 474 mMagnifedViewport.recomputeBoundsLocked(); 475 mService.scheduleAnimationLocked(); 476 } 477 onRotationChangedLocked(DisplayContent displayContent)478 public void onRotationChangedLocked(DisplayContent displayContent) { 479 if (DEBUG_ROTATION) { 480 final int rotation = displayContent.getRotation(); 481 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation) 482 + " displayId: " + displayContent.getDisplayId()); 483 } 484 mMagnifedViewport.onRotationChangedLocked(displayContent.getPendingTransaction()); 485 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED); 486 } 487 onAppWindowTransitionLocked(int displayId, int transition)488 public void onAppWindowTransitionLocked(int displayId, int transition) { 489 if (DEBUG_WINDOW_TRANSITIONS) { 490 Slog.i(LOG_TAG, "Window transition: " 491 + AppTransition.appTransitionToString(transition) 492 + " displayId: " + displayId); 493 } 494 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); 495 if (magnifying) { 496 switch (transition) { 497 case WindowManager.TRANSIT_ACTIVITY_OPEN: 498 case WindowManager.TRANSIT_TASK_OPEN: 499 case WindowManager.TRANSIT_TASK_TO_FRONT: 500 case WindowManager.TRANSIT_WALLPAPER_OPEN: 501 case WindowManager.TRANSIT_WALLPAPER_CLOSE: 502 case WindowManager.TRANSIT_WALLPAPER_INTRA_OPEN: { 503 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED); 504 } 505 } 506 } 507 } 508 onWindowTransitionLocked(WindowState windowState, int transition)509 public void onWindowTransitionLocked(WindowState windowState, int transition) { 510 if (DEBUG_WINDOW_TRANSITIONS) { 511 Slog.i(LOG_TAG, "Window transition: " 512 + AppTransition.appTransitionToString(transition) 513 + " displayId: " + windowState.getDisplayId()); 514 } 515 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked(); 516 final int type = windowState.mAttrs.type; 517 switch (transition) { 518 case WindowManagerPolicy.TRANSIT_ENTER: 519 case WindowManagerPolicy.TRANSIT_SHOW: { 520 if (!magnifying) { 521 break; 522 } 523 switch (type) { 524 case WindowManager.LayoutParams.TYPE_APPLICATION: 525 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION: 526 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL: 527 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA: 528 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL: 529 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL: 530 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG: 531 case WindowManager.LayoutParams.TYPE_SEARCH_BAR: 532 case WindowManager.LayoutParams.TYPE_PHONE: 533 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT: 534 case WindowManager.LayoutParams.TYPE_TOAST: 535 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY: 536 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY: 537 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE: 538 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG: 539 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG: 540 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR: 541 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY: 542 case WindowManager.LayoutParams.TYPE_QS_DIALOG: 543 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: { 544 Rect magnifiedRegionBounds = mTempRect2; 545 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked( 546 magnifiedRegionBounds); 547 Rect touchableRegionBounds = mTempRect1; 548 windowState.getTouchableRegion(mTempRegion1); 549 mTempRegion1.getBounds(touchableRegionBounds); 550 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) { 551 mCallbacks.onRectangleOnScreenRequested( 552 touchableRegionBounds.left, 553 touchableRegionBounds.top, 554 touchableRegionBounds.right, 555 touchableRegionBounds.bottom); 556 } 557 } break; 558 } break; 559 } 560 } 561 } 562 getMagnificationSpecForWindowLocked(WindowState windowState)563 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) { 564 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked(); 565 if (spec != null && !spec.isNop()) { 566 if (!windowState.shouldMagnify()) { 567 return null; 568 } 569 } 570 return spec; 571 } 572 getMagnificationRegionLocked(Region outMagnificationRegion)573 public void getMagnificationRegionLocked(Region outMagnificationRegion) { 574 // Make sure we're working with the most current bounds 575 mMagnifedViewport.recomputeBoundsLocked(); 576 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion); 577 } 578 destroyLocked()579 public void destroyLocked() { 580 mMagnifedViewport.destroyWindow(); 581 } 582 583 // Can be called outside of a surface transaction showMagnificationBoundsIfNeeded()584 public void showMagnificationBoundsIfNeeded() { 585 mHandler.obtainMessage(MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED) 586 .sendToTarget(); 587 } 588 drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t)589 public void drawMagnifiedRegionBorderIfNeededLocked(SurfaceControl.Transaction t) { 590 mMagnifedViewport.drawWindowIfNeededLocked(t); 591 } 592 dump(PrintWriter pw, String prefix)593 void dump(PrintWriter pw, String prefix) { 594 mMagnifedViewport.dump(pw, prefix); 595 } 596 597 private final class MagnifiedViewport { 598 599 private final SparseArray<WindowState> mTempWindowStates = 600 new SparseArray<WindowState>(); 601 602 private final RectF mTempRectF = new RectF(); 603 604 private final Point mTempPoint = new Point(); 605 606 private final Matrix mTempMatrix = new Matrix(); 607 608 private final Region mMagnificationRegion = new Region(); 609 private final Region mOldMagnificationRegion = new Region(); 610 611 private final Path mCircularPath; 612 613 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain(); 614 615 private final float mBorderWidth; 616 private final int mHalfBorderWidth; 617 private final int mDrawBorderInset; 618 619 private final ViewportWindow mWindow; 620 621 private boolean mFullRedrawNeeded; 622 private int mTempLayer = 0; 623 MagnifiedViewport()624 public MagnifiedViewport() { 625 mBorderWidth = mDisplayContext.getResources().getDimension( 626 com.android.internal.R.dimen.accessibility_magnification_indicator_width); 627 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2); 628 mDrawBorderInset = (int) mBorderWidth / 2; 629 mWindow = new ViewportWindow(mDisplayContext); 630 631 if (mDisplayContext.getResources().getConfiguration().isScreenRound()) { 632 mCircularPath = new Path(); 633 mDisplay.getRealSize(mTempPoint); 634 final int centerXY = mTempPoint.x / 2; 635 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW); 636 } else { 637 mCircularPath = null; 638 } 639 640 recomputeBoundsLocked(); 641 } 642 getMagnificationRegionLocked(@onNull Region outMagnificationRegion)643 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) { 644 outMagnificationRegion.set(mMagnificationRegion); 645 } 646 updateMagnificationSpecLocked(MagnificationSpec spec)647 public void updateMagnificationSpecLocked(MagnificationSpec spec) { 648 if (spec != null) { 649 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY); 650 } else { 651 mMagnificationSpec.clear(); 652 } 653 // If this message is pending we are in a rotation animation and do not want 654 // to show the border. We will do so when the pending message is handled. 655 if (!mHandler.hasMessages( 656 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) { 657 setMagnifiedRegionBorderShownLocked( 658 isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked(), true); 659 } 660 } 661 recomputeBoundsLocked()662 public void recomputeBoundsLocked() { 663 mDisplay.getRealSize(mTempPoint); 664 final int screenWidth = mTempPoint.x; 665 final int screenHeight = mTempPoint.y; 666 667 mMagnificationRegion.set(0, 0, 0, 0); 668 final Region availableBounds = mTempRegion1; 669 availableBounds.set(0, 0, screenWidth, screenHeight); 670 671 if (mCircularPath != null) { 672 availableBounds.setPath(mCircularPath, availableBounds); 673 } 674 675 Region nonMagnifiedBounds = mTempRegion4; 676 nonMagnifiedBounds.set(0, 0, 0, 0); 677 678 SparseArray<WindowState> visibleWindows = mTempWindowStates; 679 visibleWindows.clear(); 680 populateWindowsOnScreenLocked(visibleWindows); 681 682 final int visibleWindowCount = visibleWindows.size(); 683 for (int i = visibleWindowCount - 1; i >= 0; i--) { 684 WindowState windowState = visibleWindows.valueAt(i); 685 if ((windowState.mAttrs.type == TYPE_MAGNIFICATION_OVERLAY) 686 || ((windowState.mAttrs.privateFlags 687 & PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY) != 0)) { 688 continue; 689 } 690 691 // Consider the touchable portion of the window 692 Matrix matrix = mTempMatrix; 693 populateTransformationMatrixLocked(windowState, matrix); 694 Region touchableRegion = mTempRegion3; 695 windowState.getTouchableRegion(touchableRegion); 696 Rect touchableFrame = mTempRect1; 697 touchableRegion.getBounds(touchableFrame); 698 RectF windowFrame = mTempRectF; 699 windowFrame.set(touchableFrame); 700 windowFrame.offset(-windowState.getFrameLw().left, 701 -windowState.getFrameLw().top); 702 matrix.mapRect(windowFrame); 703 Region windowBounds = mTempRegion2; 704 windowBounds.set((int) windowFrame.left, (int) windowFrame.top, 705 (int) windowFrame.right, (int) windowFrame.bottom); 706 // Only update new regions 707 Region portionOfWindowAlreadyAccountedFor = mTempRegion3; 708 portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion); 709 portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION); 710 windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE); 711 712 if (windowState.shouldMagnify()) { 713 mMagnificationRegion.op(windowBounds, Region.Op.UNION); 714 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT); 715 } else { 716 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION); 717 availableBounds.op(windowBounds, Region.Op.DIFFERENCE); 718 } 719 720 // If the navigation bar window doesn't have touchable region, count 721 // navigation bar insets into nonMagnifiedBounds. It happens when 722 // navigation mode is gestural. 723 if (isUntouchableNavigationBar(windowState, mTempRegion3)) { 724 final Rect navBarInsets = getNavBarInsets(mDisplayContent); 725 nonMagnifiedBounds.op(navBarInsets, Region.Op.UNION); 726 availableBounds.op(navBarInsets, Region.Op.DIFFERENCE); 727 } 728 729 // Count letterbox into nonMagnifiedBounds 730 if (windowState.isLetterboxedForDisplayCutoutLw()) { 731 Region letterboxBounds = getLetterboxBounds(windowState); 732 nonMagnifiedBounds.op(letterboxBounds, Region.Op.UNION); 733 availableBounds.op(letterboxBounds, Region.Op.DIFFERENCE); 734 } 735 736 // Update accounted bounds 737 Region accountedBounds = mTempRegion2; 738 accountedBounds.set(mMagnificationRegion); 739 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION); 740 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT); 741 742 if (accountedBounds.isRect()) { 743 Rect accountedFrame = mTempRect1; 744 accountedBounds.getBounds(accountedFrame); 745 if (accountedFrame.width() == screenWidth 746 && accountedFrame.height() == screenHeight) { 747 break; 748 } 749 } 750 } 751 752 visibleWindows.clear(); 753 754 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset, 755 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset, 756 Region.Op.INTERSECT); 757 758 final boolean magnifiedChanged = 759 !mOldMagnificationRegion.equals(mMagnificationRegion); 760 if (magnifiedChanged) { 761 mWindow.setBounds(mMagnificationRegion); 762 final Rect dirtyRect = mTempRect1; 763 if (mFullRedrawNeeded) { 764 mFullRedrawNeeded = false; 765 dirtyRect.set(mDrawBorderInset, mDrawBorderInset, 766 screenWidth - mDrawBorderInset, 767 screenHeight - mDrawBorderInset); 768 mWindow.invalidate(dirtyRect); 769 } else { 770 final Region dirtyRegion = mTempRegion3; 771 dirtyRegion.set(mMagnificationRegion); 772 dirtyRegion.op(mOldMagnificationRegion, Region.Op.XOR); 773 dirtyRegion.getBounds(dirtyRect); 774 mWindow.invalidate(dirtyRect); 775 } 776 777 mOldMagnificationRegion.set(mMagnificationRegion); 778 final SomeArgs args = SomeArgs.obtain(); 779 args.arg1 = Region.obtain(mMagnificationRegion); 780 mHandler.obtainMessage( 781 MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args) 782 .sendToTarget(); 783 } 784 } 785 getLetterboxBounds(WindowState windowState)786 private Region getLetterboxBounds(WindowState windowState) { 787 final ActivityRecord appToken = windowState.mActivityRecord; 788 if (appToken == null) { 789 return new Region(); 790 } 791 792 mDisplay.getRealSize(mTempPoint); 793 final Rect letterboxInsets = appToken.getLetterboxInsets(); 794 final int screenWidth = mTempPoint.x; 795 final int screenHeight = mTempPoint.y; 796 final Rect nonLetterboxRect = mTempRect1; 797 final Region letterboxBounds = mTempRegion3; 798 nonLetterboxRect.set(0, 0, screenWidth, screenHeight); 799 nonLetterboxRect.inset(letterboxInsets); 800 letterboxBounds.set(0, 0, screenWidth, screenHeight); 801 letterboxBounds.op(nonLetterboxRect, Region.Op.DIFFERENCE); 802 return letterboxBounds; 803 } 804 onRotationChangedLocked(SurfaceControl.Transaction t)805 public void onRotationChangedLocked(SurfaceControl.Transaction t) { 806 // If we are showing the magnification border, hide it immediately so 807 // the user does not see strange artifacts during rotation. The screenshot 808 // used for rotation already has the border. After the rotation is complete 809 // we will show the border. 810 if (isMagnifyingLocked() || isForceShowingMagnifiableBoundsLocked()) { 811 setMagnifiedRegionBorderShownLocked(false, false); 812 final long delay = (long) (mLongAnimationDuration 813 * mService.getWindowAnimationScaleLocked()); 814 Message message = mHandler.obtainMessage( 815 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED); 816 mHandler.sendMessageDelayed(message, delay); 817 } 818 recomputeBoundsLocked(); 819 mWindow.updateSize(t); 820 } 821 setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate)822 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) { 823 if (shown) { 824 mFullRedrawNeeded = true; 825 mOldMagnificationRegion.set(0, 0, 0, 0); 826 } 827 mWindow.setShown(shown, animate); 828 } 829 getMagnifiedFrameInContentCoordsLocked(Rect rect)830 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) { 831 MagnificationSpec spec = mMagnificationSpec; 832 mMagnificationRegion.getBounds(rect); 833 rect.offset((int) -spec.offsetX, (int) -spec.offsetY); 834 rect.scale(1.0f / spec.scale); 835 } 836 isMagnifyingLocked()837 public boolean isMagnifyingLocked() { 838 return mMagnificationSpec.scale > 1.0f; 839 } 840 getMagnificationSpecLocked()841 public MagnificationSpec getMagnificationSpecLocked() { 842 return mMagnificationSpec; 843 } 844 drawWindowIfNeededLocked(SurfaceControl.Transaction t)845 public void drawWindowIfNeededLocked(SurfaceControl.Transaction t) { 846 recomputeBoundsLocked(); 847 mWindow.drawIfNeeded(t); 848 } 849 destroyWindow()850 public void destroyWindow() { 851 mWindow.releaseSurface(); 852 } 853 populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows)854 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { 855 mTempLayer = 0; 856 mDisplayContent.forAllWindows((w) -> { 857 if (w.isOnScreen() && w.isVisibleLw() 858 && (w.mAttrs.alpha != 0)) { 859 mTempLayer++; 860 outWindows.put(mTempLayer, w); 861 } 862 }, false /* traverseTopToBottom */ ); 863 } 864 dump(PrintWriter pw, String prefix)865 void dump(PrintWriter pw, String prefix) { 866 mWindow.dump(pw, prefix); 867 } 868 869 private final class ViewportWindow { 870 private static final String SURFACE_TITLE = "Magnification Overlay"; 871 872 private final Region mBounds = new Region(); 873 private final Rect mDirtyRect = new Rect(); 874 private final Paint mPaint = new Paint(); 875 876 private final SurfaceControl mSurfaceControl; 877 private final Surface mSurface = mService.mSurfaceFactory.get(); 878 879 private final AnimationController mAnimationController; 880 881 private boolean mShown; 882 private int mAlpha; 883 884 private boolean mInvalidated; 885 ViewportWindow(Context context)886 public ViewportWindow(Context context) { 887 SurfaceControl surfaceControl = null; 888 try { 889 mDisplay.getRealSize(mTempPoint); 890 surfaceControl = mDisplayContent 891 .makeOverlay() 892 .setName(SURFACE_TITLE) 893 .setBufferSize(mTempPoint.x, mTempPoint.y) // not a typo 894 .setFormat(PixelFormat.TRANSLUCENT) 895 .setCallsite("ViewportWindow") 896 .build(); 897 } catch (OutOfResourcesException oore) { 898 /* ignore */ 899 } 900 mSurfaceControl = surfaceControl; 901 902 final SurfaceControl.Transaction t = mService.mTransactionFactory.get(); 903 final int layer = 904 mService.mPolicy.getWindowLayerFromTypeLw(TYPE_MAGNIFICATION_OVERLAY) * 905 WindowManagerService.TYPE_LAYER_MULTIPLIER; 906 t.setLayer(mSurfaceControl, layer).setPosition(mSurfaceControl, 0, 0); 907 InputMonitor.setTrustedOverlayInputInfo(mSurfaceControl, t, 908 mDisplayContent.getDisplayId(), "Magnification Overlay"); 909 t.apply(); 910 911 mSurface.copyFrom(mSurfaceControl); 912 913 mAnimationController = new AnimationController(context, 914 mService.mH.getLooper()); 915 916 TypedValue typedValue = new TypedValue(); 917 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight, 918 typedValue, true); 919 final int borderColor = context.getColor(typedValue.resourceId); 920 921 mPaint.setStyle(Paint.Style.STROKE); 922 mPaint.setStrokeWidth(mBorderWidth); 923 mPaint.setColor(borderColor); 924 925 mInvalidated = true; 926 } 927 setShown(boolean shown, boolean animate)928 public void setShown(boolean shown, boolean animate) { 929 synchronized (mService.mGlobalLock) { 930 if (mShown == shown) { 931 return; 932 } 933 mShown = shown; 934 mAnimationController.onFrameShownStateChanged(shown, animate); 935 if (DEBUG_VIEWPORT_WINDOW) { 936 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown); 937 } 938 } 939 } 940 941 @SuppressWarnings("unused") 942 // Called reflectively from an animator. getAlpha()943 public int getAlpha() { 944 synchronized (mService.mGlobalLock) { 945 return mAlpha; 946 } 947 } 948 setAlpha(int alpha)949 public void setAlpha(int alpha) { 950 synchronized (mService.mGlobalLock) { 951 if (mAlpha == alpha) { 952 return; 953 } 954 mAlpha = alpha; 955 invalidate(null); 956 if (DEBUG_VIEWPORT_WINDOW) { 957 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha); 958 } 959 } 960 } 961 setBounds(Region bounds)962 public void setBounds(Region bounds) { 963 synchronized (mService.mGlobalLock) { 964 if (mBounds.equals(bounds)) { 965 return; 966 } 967 mBounds.set(bounds); 968 invalidate(mDirtyRect); 969 if (DEBUG_VIEWPORT_WINDOW) { 970 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds); 971 } 972 } 973 } 974 updateSize(SurfaceControl.Transaction t)975 public void updateSize(SurfaceControl.Transaction t) { 976 synchronized (mService.mGlobalLock) { 977 mDisplay.getRealSize(mTempPoint); 978 t.setBufferSize(mSurfaceControl, mTempPoint.x, mTempPoint.y); 979 invalidate(mDirtyRect); 980 } 981 } 982 invalidate(Rect dirtyRect)983 public void invalidate(Rect dirtyRect) { 984 if (dirtyRect != null) { 985 mDirtyRect.set(dirtyRect); 986 } else { 987 mDirtyRect.setEmpty(); 988 } 989 mInvalidated = true; 990 mService.scheduleAnimationLocked(); 991 } 992 drawIfNeeded(SurfaceControl.Transaction t)993 public void drawIfNeeded(SurfaceControl.Transaction t) { 994 synchronized (mService.mGlobalLock) { 995 if (!mInvalidated) { 996 return; 997 } 998 mInvalidated = false; 999 if (mAlpha > 0) { 1000 Canvas canvas = null; 1001 try { 1002 // Empty dirty rectangle means unspecified. 1003 if (mDirtyRect.isEmpty()) { 1004 mBounds.getBounds(mDirtyRect); 1005 } 1006 mDirtyRect.inset(-mHalfBorderWidth, -mHalfBorderWidth); 1007 canvas = mSurface.lockCanvas(mDirtyRect); 1008 if (DEBUG_VIEWPORT_WINDOW) { 1009 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect); 1010 } 1011 } catch (IllegalArgumentException iae) { 1012 /* ignore */ 1013 } catch (Surface.OutOfResourcesException oore) { 1014 /* ignore */ 1015 } 1016 if (canvas == null) { 1017 return; 1018 } 1019 if (DEBUG_VIEWPORT_WINDOW) { 1020 Slog.i(LOG_TAG, "Bounds: " + mBounds); 1021 } 1022 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR); 1023 mPaint.setAlpha(mAlpha); 1024 Path path = mBounds.getBoundaryPath(); 1025 canvas.drawPath(path, mPaint); 1026 1027 mSurface.unlockCanvasAndPost(canvas); 1028 t.show(mSurfaceControl); 1029 } else { 1030 t.hide(mSurfaceControl); 1031 } 1032 } 1033 } 1034 releaseSurface()1035 public void releaseSurface() { 1036 mService.mTransactionFactory.get().remove(mSurfaceControl).apply(); 1037 mSurface.release(); 1038 } 1039 dump(PrintWriter pw, String prefix)1040 void dump(PrintWriter pw, String prefix) { 1041 pw.println(prefix 1042 + " mBounds= " + mBounds 1043 + " mDirtyRect= " + mDirtyRect 1044 + " mWidth= " + mSurfaceControl.getWidth() 1045 + " mHeight= " + mSurfaceControl.getHeight()); 1046 } 1047 1048 private final class AnimationController extends Handler { 1049 private static final String PROPERTY_NAME_ALPHA = "alpha"; 1050 1051 private static final int MIN_ALPHA = 0; 1052 private static final int MAX_ALPHA = 255; 1053 1054 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1; 1055 1056 private final ValueAnimator mShowHideFrameAnimator; 1057 AnimationController(Context context, Looper looper)1058 public AnimationController(Context context, Looper looper) { 1059 super(looper); 1060 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this, 1061 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA); 1062 1063 Interpolator interpolator = new DecelerateInterpolator(2.5f); 1064 final long longAnimationDuration = context.getResources().getInteger( 1065 com.android.internal.R.integer.config_longAnimTime); 1066 1067 mShowHideFrameAnimator.setInterpolator(interpolator); 1068 mShowHideFrameAnimator.setDuration(longAnimationDuration); 1069 } 1070 onFrameShownStateChanged(boolean shown, boolean animate)1071 public void onFrameShownStateChanged(boolean shown, boolean animate) { 1072 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED, 1073 shown ? 1 : 0, animate ? 1 : 0).sendToTarget(); 1074 } 1075 1076 @Override handleMessage(Message message)1077 public void handleMessage(Message message) { 1078 switch (message.what) { 1079 case MSG_FRAME_SHOWN_STATE_CHANGED: { 1080 final boolean shown = message.arg1 == 1; 1081 final boolean animate = message.arg2 == 1; 1082 1083 if (animate) { 1084 if (mShowHideFrameAnimator.isRunning()) { 1085 mShowHideFrameAnimator.reverse(); 1086 } else { 1087 if (shown) { 1088 mShowHideFrameAnimator.start(); 1089 } else { 1090 mShowHideFrameAnimator.reverse(); 1091 } 1092 } 1093 } else { 1094 mShowHideFrameAnimator.cancel(); 1095 if (shown) { 1096 setAlpha(MAX_ALPHA); 1097 } else { 1098 setAlpha(MIN_ALPHA); 1099 } 1100 } 1101 } break; 1102 } 1103 } 1104 } 1105 } 1106 } 1107 1108 private class MyHandler extends Handler { 1109 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1; 1110 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2; 1111 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3; 1112 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4; 1113 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5; 1114 MyHandler(Looper looper)1115 public MyHandler(Looper looper) { 1116 super(looper); 1117 } 1118 1119 @Override handleMessage(Message message)1120 public void handleMessage(Message message) { 1121 switch (message.what) { 1122 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: { 1123 final SomeArgs args = (SomeArgs) message.obj; 1124 final Region magnifiedBounds = (Region) args.arg1; 1125 mCallbacks.onMagnificationRegionChanged(magnifiedBounds); 1126 magnifiedBounds.recycle(); 1127 } break; 1128 1129 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: { 1130 SomeArgs args = (SomeArgs) message.obj; 1131 final int left = args.argi1; 1132 final int top = args.argi2; 1133 final int right = args.argi3; 1134 final int bottom = args.argi4; 1135 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom); 1136 args.recycle(); 1137 } break; 1138 1139 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: { 1140 mCallbacks.onUserContextChanged(); 1141 } break; 1142 1143 case MESSAGE_NOTIFY_ROTATION_CHANGED: { 1144 final int rotation = message.arg1; 1145 mCallbacks.onRotationChanged(rotation); 1146 } break; 1147 1148 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : { 1149 synchronized (mService.mGlobalLock) { 1150 if (mMagnifedViewport.isMagnifyingLocked() 1151 || isForceShowingMagnifiableBoundsLocked()) { 1152 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true); 1153 mService.scheduleAnimationLocked(); 1154 } 1155 } 1156 } break; 1157 } 1158 } 1159 } 1160 } 1161 isUntouchableNavigationBar(WindowState windowState, Region touchableRegion)1162 static boolean isUntouchableNavigationBar(WindowState windowState, 1163 Region touchableRegion) { 1164 if (windowState.mAttrs.type != WindowManager.LayoutParams.TYPE_NAVIGATION_BAR) { 1165 return false; 1166 } 1167 1168 // Gets the touchable region. 1169 windowState.getTouchableRegion(touchableRegion); 1170 1171 return touchableRegion.isEmpty(); 1172 } 1173 getNavBarInsets(DisplayContent displayContent)1174 static Rect getNavBarInsets(DisplayContent displayContent) { 1175 final InsetsSource source = displayContent.getInsetsStateController().getRawInsetsState() 1176 .peekSource(ITYPE_NAVIGATION_BAR); 1177 return source != null ? source.getFrame() : EMPTY_RECT; 1178 } 1179 1180 /** 1181 * This class encapsulates the functionality related to computing the windows 1182 * reported for accessibility purposes. These windows are all windows a sighted 1183 * user can see on the screen. 1184 */ 1185 private static final class WindowsForAccessibilityObserver { 1186 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? 1187 "WindowsForAccessibilityObserver" : TAG_WM; 1188 1189 private static final boolean DEBUG = false; 1190 1191 private final SparseArray<WindowState> mTempWindowStates = new SparseArray<>(); 1192 1193 private final Set<IBinder> mTempBinderSet = new ArraySet<>(); 1194 1195 private final RectF mTempRectF = new RectF(); 1196 1197 private final Matrix mTempMatrix = new Matrix(); 1198 1199 private final Point mTempPoint = new Point(); 1200 1201 private final Region mTempRegion = new Region(); 1202 1203 private final Region mTempRegion1 = new Region(); 1204 1205 private final WindowManagerService mService; 1206 1207 private final Handler mHandler; 1208 1209 private final WindowsForAccessibilityCallback mCallback; 1210 1211 private final int mDisplayId; 1212 1213 private final long mRecurringAccessibilityEventsIntervalMillis; 1214 1215 private final IntArray mEmbeddedDisplayIdList = new IntArray(0); 1216 WindowsForAccessibilityObserver(WindowManagerService windowManagerService, int displayId, WindowsForAccessibilityCallback callback)1217 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService, 1218 int displayId, 1219 WindowsForAccessibilityCallback callback) { 1220 mService = windowManagerService; 1221 mCallback = callback; 1222 mDisplayId = displayId; 1223 mHandler = new MyHandler(mService.mH.getLooper()); 1224 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration 1225 .getSendRecurringAccessibilityEventsInterval(); 1226 computeChangedWindows(true); 1227 } 1228 performComputeChangedWindowsNotLocked(boolean forceSend)1229 public void performComputeChangedWindowsNotLocked(boolean forceSend) { 1230 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS); 1231 computeChangedWindows(forceSend); 1232 } 1233 scheduleComputeChangedWindowsLocked()1234 public void scheduleComputeChangedWindowsLocked() { 1235 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) { 1236 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS, 1237 mRecurringAccessibilityEventsIntervalMillis); 1238 } 1239 } 1240 getAndClearEmbeddedDisplayIdList()1241 IntArray getAndClearEmbeddedDisplayIdList() { 1242 final IntArray returnedArray = new IntArray(mEmbeddedDisplayIdList.size()); 1243 returnedArray.addAll(mEmbeddedDisplayIdList); 1244 mEmbeddedDisplayIdList.clear(); 1245 1246 return returnedArray; 1247 } 1248 addEmbeddedDisplay(int displayId)1249 void addEmbeddedDisplay(int displayId) { 1250 if (displayId == mDisplayId) { 1251 return; 1252 } 1253 mEmbeddedDisplayIdList.add(displayId); 1254 } 1255 1256 /** 1257 * Check if windows have changed, and send them to the accessibility subsystem if they have. 1258 * 1259 * @param forceSend Send the windows the accessibility even if they haven't changed. 1260 */ computeChangedWindows(boolean forceSend)1261 public void computeChangedWindows(boolean forceSend) { 1262 if (DEBUG) { 1263 Slog.i(LOG_TAG, "computeChangedWindows()"); 1264 } 1265 1266 List<WindowInfo> windows = new ArrayList<>(); 1267 final int topFocusedDisplayId; 1268 IBinder topFocusedWindowToken = null; 1269 1270 synchronized (mService.mGlobalLock) { 1271 // Do not send the windows if there is no top focus as 1272 // the window manager is still looking for where to put it. 1273 // We will do the work when we get a focus change callback. 1274 final WindowState topFocusedWindowState = getTopFocusWindow(); 1275 if (topFocusedWindowState == null) return; 1276 1277 final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); 1278 if (dc == null) { 1279 return; 1280 } 1281 final Display display = dc.getDisplay(); 1282 display.getRealSize(mTempPoint); 1283 final int screenWidth = mTempPoint.x; 1284 final int screenHeight = mTempPoint.y; 1285 1286 Region unaccountedSpace = mTempRegion; 1287 unaccountedSpace.set(0, 0, screenWidth, screenHeight); 1288 1289 final SparseArray<WindowState> visibleWindows = mTempWindowStates; 1290 populateVisibleWindowsOnScreenLocked(visibleWindows); 1291 Set<IBinder> addedWindows = mTempBinderSet; 1292 addedWindows.clear(); 1293 1294 boolean focusedWindowAdded = false; 1295 1296 final int visibleWindowCount = visibleWindows.size(); 1297 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>(); 1298 1299 // Iterate until we figure out what is touchable for the entire screen. 1300 for (int i = visibleWindowCount - 1; i >= 0; i--) { 1301 final WindowState windowState = visibleWindows.valueAt(i); 1302 final Region regionInScreen = new Region(); 1303 computeWindowRegionInScreen(windowState, regionInScreen); 1304 1305 if (windowMattersToAccessibility(windowState, regionInScreen, unaccountedSpace, 1306 skipRemainingWindowsForTasks)) { 1307 addPopulatedWindowInfo(windowState, regionInScreen, windows, addedWindows); 1308 updateUnaccountedSpace(windowState, regionInScreen, unaccountedSpace, 1309 skipRemainingWindowsForTasks); 1310 focusedWindowAdded |= windowState.isFocused(); 1311 } else if (isUntouchableNavigationBar(windowState, mTempRegion1)) { 1312 // If this widow is navigation bar without touchable region, accounting the 1313 // region of navigation bar inset because all touch events from this region 1314 // would be received by launcher, i.e. this region is a un-touchable one 1315 // for the application. 1316 unaccountedSpace.op(getNavBarInsets(dc), unaccountedSpace, 1317 Region.Op.REVERSE_DIFFERENCE); 1318 } 1319 1320 if (unaccountedSpace.isEmpty() && focusedWindowAdded) { 1321 break; 1322 } 1323 } 1324 1325 for (int i = dc.mShellRoots.size() - 1; i >= 0; --i) { 1326 final WindowInfo info = dc.mShellRoots.valueAt(i).getWindowInfo(); 1327 if (info == null) { 1328 continue; 1329 } 1330 info.layer = addedWindows.size(); 1331 windows.add(info); 1332 addedWindows.add(info.token); 1333 } 1334 1335 // Remove child/parent references to windows that were not added. 1336 final int windowCount = windows.size(); 1337 for (int i = 0; i < windowCount; i++) { 1338 WindowInfo window = windows.get(i); 1339 if (!addedWindows.contains(window.parentToken)) { 1340 window.parentToken = null; 1341 } 1342 if (window.childTokens != null) { 1343 final int childTokenCount = window.childTokens.size(); 1344 for (int j = childTokenCount - 1; j >= 0; j--) { 1345 if (!addedWindows.contains(window.childTokens.get(j))) { 1346 window.childTokens.remove(j); 1347 } 1348 } 1349 // Leave the child token list if empty. 1350 } 1351 } 1352 1353 visibleWindows.clear(); 1354 addedWindows.clear(); 1355 1356 // Gets the top focused display Id and window token for supporting multi-display. 1357 topFocusedDisplayId = mService.mRoot.getTopFocusedDisplayContent().getDisplayId(); 1358 topFocusedWindowToken = topFocusedWindowState.mClient.asBinder(); 1359 } 1360 mCallback.onWindowsForAccessibilityChanged(forceSend, topFocusedDisplayId, 1361 topFocusedWindowToken, windows); 1362 1363 // Recycle the windows as we do not need them. 1364 clearAndRecycleWindows(windows); 1365 } 1366 windowMattersToAccessibility(WindowState windowState, Region regionInScreen, Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks)1367 private boolean windowMattersToAccessibility(WindowState windowState, 1368 Region regionInScreen, Region unaccountedSpace, 1369 HashSet<Integer> skipRemainingWindowsForTasks) { 1370 final RecentsAnimationController controller = mService.getRecentsAnimationController(); 1371 if (controller != null && controller.shouldIgnoreForAccessibility(windowState)) { 1372 return false; 1373 } 1374 1375 if (windowState.isFocused()) { 1376 return true; 1377 } 1378 1379 // If the window is part of a task that we're finished with - ignore. 1380 final Task task = windowState.getTask(); 1381 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) { 1382 return false; 1383 } 1384 1385 // Ignore non-touchable windows, except the split-screen divider, which is 1386 // occasionally non-touchable but still useful for identifying split-screen 1387 // mode. 1388 if (((windowState.mAttrs.flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) 1389 && (windowState.mAttrs.type != TYPE_DOCK_DIVIDER)) { 1390 return false; 1391 } 1392 1393 // If the window is completely covered by other windows - ignore. 1394 if (unaccountedSpace.quickReject(regionInScreen)) { 1395 return false; 1396 } 1397 1398 // Add windows of certain types not covered by modal windows. 1399 if (isReportedWindowType(windowState.mAttrs.type)) { 1400 return true; 1401 } 1402 1403 return false; 1404 } 1405 updateUnaccountedSpace(WindowState windowState, Region regionInScreen, Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks)1406 private void updateUnaccountedSpace(WindowState windowState, Region regionInScreen, 1407 Region unaccountedSpace, HashSet<Integer> skipRemainingWindowsForTasks) { 1408 if (windowState.mAttrs.type 1409 != WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) { 1410 1411 // Account for the space this window takes if the window 1412 // is not an accessibility overlay which does not change 1413 // the reported windows. 1414 unaccountedSpace.op(regionInScreen, unaccountedSpace, 1415 Region.Op.REVERSE_DIFFERENCE); 1416 1417 // If a window is modal it prevents other windows from being touched 1418 if ((windowState.mAttrs.flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE 1419 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) { 1420 if (!windowState.hasTapExcludeRegion()) { 1421 // Account for all space in the task, whether the windows in it are 1422 // touchable or not. The modal window blocks all touches from the task's 1423 // area. 1424 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace, 1425 Region.Op.REVERSE_DIFFERENCE); 1426 } else { 1427 // If a window has tap exclude region, we need to account it. 1428 final Region displayRegion = new Region(windowState.getDisplayFrameLw()); 1429 final Region tapExcludeRegion = new Region(); 1430 windowState.getTapExcludeRegion(tapExcludeRegion); 1431 displayRegion.op(tapExcludeRegion, displayRegion, 1432 Region.Op.REVERSE_DIFFERENCE); 1433 unaccountedSpace.op(displayRegion, unaccountedSpace, 1434 Region.Op.REVERSE_DIFFERENCE); 1435 } 1436 1437 final Task task = windowState.getTask(); 1438 if (task != null) { 1439 // If the window is associated with a particular task, we can skip the 1440 // rest of the windows for that task. 1441 skipRemainingWindowsForTasks.add(task.mTaskId); 1442 } else if (!windowState.hasTapExcludeRegion()) { 1443 // If the window is not associated with a particular task, then it is 1444 // globally modal. In this case we can skip all remaining windows when 1445 // it doesn't has tap exclude region. 1446 unaccountedSpace.setEmpty(); 1447 } 1448 } 1449 } 1450 } 1451 computeWindowRegionInScreen(WindowState windowState, Region outRegion)1452 private void computeWindowRegionInScreen(WindowState windowState, Region outRegion) { 1453 // Get the touchable frame. 1454 Region touchableRegion = mTempRegion1; 1455 windowState.getTouchableRegion(touchableRegion); 1456 1457 // Map the frame to get what appears on the screen. 1458 Matrix matrix = mTempMatrix; 1459 populateTransformationMatrixLocked(windowState, matrix); 1460 1461 forEachRect(touchableRegion, rect -> { 1462 // Move to origin as all transforms are captured by the matrix. 1463 RectF windowFrame = mTempRectF; 1464 windowFrame.set(rect); 1465 windowFrame.offset(-windowState.getFrameLw().left, -windowState.getFrameLw().top); 1466 1467 matrix.mapRect(windowFrame); 1468 1469 // Union all rects. 1470 outRegion.union(new Rect((int) windowFrame.left, (int) windowFrame.top, 1471 (int) windowFrame.right, (int) windowFrame.bottom)); 1472 }); 1473 } 1474 addPopulatedWindowInfo(WindowState windowState, Region regionInScreen, List<WindowInfo> out, Set<IBinder> tokenOut)1475 private static void addPopulatedWindowInfo(WindowState windowState, Region regionInScreen, 1476 List<WindowInfo> out, Set<IBinder> tokenOut) { 1477 final WindowInfo window = windowState.getWindowInfo(); 1478 window.regionInScreen.set(regionInScreen); 1479 window.layer = tokenOut.size(); 1480 out.add(window); 1481 tokenOut.add(window.token); 1482 } 1483 clearAndRecycleWindows(List<WindowInfo> windows)1484 private static void clearAndRecycleWindows(List<WindowInfo> windows) { 1485 final int windowCount = windows.size(); 1486 for (int i = windowCount - 1; i >= 0; i--) { 1487 windows.remove(i).recycle(); 1488 } 1489 } 1490 isReportedWindowType(int windowType)1491 private static boolean isReportedWindowType(int windowType) { 1492 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER 1493 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS 1494 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY 1495 && windowType != WindowManager.LayoutParams.TYPE_DRAG 1496 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER 1497 && windowType != WindowManager.LayoutParams.TYPE_POINTER 1498 && windowType != TYPE_MAGNIFICATION_OVERLAY 1499 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY 1500 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY 1501 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION); 1502 } 1503 populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows)1504 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) { 1505 final List<WindowState> tempWindowStatesList = new ArrayList<>(); 1506 final DisplayContent dc = mService.mRoot.getDisplayContent(mDisplayId); 1507 if (dc == null) { 1508 return; 1509 } 1510 1511 dc.forAllWindows(w -> { 1512 if (w.isVisibleLw()) { 1513 tempWindowStatesList.add(w); 1514 } 1515 }, false /* traverseTopToBottom */); 1516 // Insert the re-parented windows in another display below their parents in 1517 // default display. 1518 mService.mRoot.forAllWindows(w -> { 1519 final WindowState parentWindow = findRootDisplayParentWindow(w); 1520 if (parentWindow == null) { 1521 return; 1522 } 1523 1524 if (w.isVisibleLw() && tempWindowStatesList.contains(parentWindow)) { 1525 tempWindowStatesList.add(tempWindowStatesList.lastIndexOf(parentWindow), w); 1526 } 1527 }, false /* traverseTopToBottom */); 1528 for (int i = 0; i < tempWindowStatesList.size(); i++) { 1529 outWindows.put(i, tempWindowStatesList.get(i)); 1530 } 1531 } 1532 findRootDisplayParentWindow(WindowState win)1533 private WindowState findRootDisplayParentWindow(WindowState win) { 1534 WindowState displayParentWindow = win.getDisplayContent().getParentWindow(); 1535 if (displayParentWindow == null) { 1536 return null; 1537 } 1538 WindowState candidate = displayParentWindow; 1539 while (candidate != null) { 1540 displayParentWindow = candidate; 1541 candidate = displayParentWindow.getDisplayContent().getParentWindow(); 1542 } 1543 return displayParentWindow; 1544 } 1545 getTopFocusWindow()1546 private WindowState getTopFocusWindow() { 1547 return mService.mRoot.getTopFocusedDisplayContent().mCurrentFocus; 1548 } 1549 1550 private class MyHandler extends Handler { 1551 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1; 1552 MyHandler(Looper looper)1553 public MyHandler(Looper looper) { 1554 super(looper, null, false); 1555 } 1556 1557 @Override 1558 @SuppressWarnings("unchecked") handleMessage(Message message)1559 public void handleMessage(Message message) { 1560 switch (message.what) { 1561 case MESSAGE_COMPUTE_CHANGED_WINDOWS: { 1562 computeChangedWindows(false); 1563 } break; 1564 } 1565 } 1566 } 1567 } 1568 } 1569