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