blob: 1510dd126c74c55cc9b1094776ea3d4edaccf715 [file] [log] [blame]
Svetoslav8e3feb12014-02-24 13:46:47 -08001/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.wm;
18
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -080019import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
21
Svetoslav8e3feb12014-02-24 13:46:47 -080022import android.animation.ObjectAnimator;
23import android.animation.ValueAnimator;
Alan Viverette59e53a12016-03-28 13:41:32 -040024import android.annotation.NonNull;
Svetoslav8e3feb12014-02-24 13:46:47 -080025import android.app.Service;
26import android.content.Context;
27import android.graphics.Canvas;
28import android.graphics.Color;
29import android.graphics.Matrix;
30import android.graphics.Paint;
31import android.graphics.Path;
32import android.graphics.PixelFormat;
33import android.graphics.Point;
34import android.graphics.PorterDuff.Mode;
35import android.graphics.Rect;
36import android.graphics.RectF;
37import android.graphics.Region;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Looper;
41import android.os.Message;
Phil Weaver396d5492016-03-22 17:53:50 -070042import android.text.TextUtils;
Svetoslav8e3feb12014-02-24 13:46:47 -080043import android.util.ArraySet;
44import android.util.Log;
45import android.util.Slog;
46import android.util.SparseArray;
47import android.util.TypedValue;
48import android.view.MagnificationSpec;
49import android.view.Surface;
50import android.view.Surface.OutOfResourcesException;
51import android.view.SurfaceControl;
Svetoslavf7174e82014-06-12 11:29:35 -070052import android.view.ViewConfiguration;
Svetoslav8e3feb12014-02-24 13:46:47 -080053import android.view.WindowInfo;
54import android.view.WindowManager;
55import android.view.WindowManagerInternal.MagnificationCallbacks;
56import android.view.WindowManagerInternal.WindowsForAccessibilityCallback;
57import android.view.WindowManagerPolicy;
58import android.view.animation.DecelerateInterpolator;
59import android.view.animation.Interpolator;
60
61import com.android.internal.R;
62import com.android.internal.os.SomeArgs;
63
64import java.util.ArrayList;
Allen Hairf20ac2c2016-02-11 17:42:59 -080065import java.util.HashSet;
Svetoslav8e3feb12014-02-24 13:46:47 -080066import java.util.List;
67import java.util.Set;
68
69/**
70 * This class contains the accessibility related logic of the window manger.
71 */
72final class AccessibilityController {
73
74 private final WindowManagerService mWindowManagerService;
75
76 private static final float[] sTempFloats = new float[9];
77
78 public AccessibilityController(WindowManagerService service) {
79 mWindowManagerService = service;
80 }
81
82 private DisplayMagnifier mDisplayMagnifier;
83
84 private WindowsForAccessibilityObserver mWindowsForAccessibilityObserver;
85
86 public void setMagnificationCallbacksLocked(MagnificationCallbacks callbacks) {
87 if (callbacks != null) {
88 if (mDisplayMagnifier != null) {
89 throw new IllegalStateException("Magnification callbacks already set!");
90 }
91 mDisplayMagnifier = new DisplayMagnifier(mWindowManagerService, callbacks);
92 } else {
93 if (mDisplayMagnifier == null) {
94 throw new IllegalStateException("Magnification callbacks already cleared!");
95 }
96 mDisplayMagnifier.destroyLocked();
97 mDisplayMagnifier = null;
98 }
99 }
100
101 public void setWindowsForAccessibilityCallback(WindowsForAccessibilityCallback callback) {
102 if (callback != null) {
103 if (mWindowsForAccessibilityObserver != null) {
104 throw new IllegalStateException(
105 "Windows for accessibility callback already set!");
106 }
107 mWindowsForAccessibilityObserver = new WindowsForAccessibilityObserver(
108 mWindowManagerService, callback);
109 } else {
110 if (mWindowsForAccessibilityObserver == null) {
111 throw new IllegalStateException(
112 "Windows for accessibility callback already cleared!");
113 }
114 mWindowsForAccessibilityObserver = null;
115 }
116 }
117
Svetoslav Ganovfd138892016-07-13 18:20:42 -0700118 public void performComputeChangedWindowsNotLocked() {
119 WindowsForAccessibilityObserver observer = null;
120 synchronized (mWindowManagerService) {
121 observer = mWindowsForAccessibilityObserver;
122 }
123 if (observer != null) {
124 observer.performComputeChangedWindowsNotLocked();
125 }
126 }
127
Svetoslav8e3feb12014-02-24 13:46:47 -0800128 public void setMagnificationSpecLocked(MagnificationSpec spec) {
129 if (mDisplayMagnifier != null) {
130 mDisplayMagnifier.setMagnificationSpecLocked(spec);
131 }
132 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700133 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800134 }
135 }
136
Phil Weaver70439242016-03-10 15:15:49 -0800137 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
Alan Viverette59e53a12016-03-28 13:41:32 -0400138 if (mDisplayMagnifier != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800139 mDisplayMagnifier.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400140 }
141 }
142
Svetoslavf7174e82014-06-12 11:29:35 -0700143 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800144 if (mDisplayMagnifier != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700145 mDisplayMagnifier.onRectangleOnScreenRequestedLocked(rectangle);
Svetoslav8e3feb12014-02-24 13:46:47 -0800146 }
147 // Not relevant for the window observer.
148 }
149
150 public void onWindowLayersChangedLocked() {
151 if (mDisplayMagnifier != null) {
152 mDisplayMagnifier.onWindowLayersChangedLocked();
153 }
154 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700155 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800156 }
157 }
158
Andrii Kulian8ee72852017-03-10 10:36:45 -0800159 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800160 if (mDisplayMagnifier != null) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800161 mDisplayMagnifier.onRotationChangedLocked(displayContent);
Svetoslav8e3feb12014-02-24 13:46:47 -0800162 }
163 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700164 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800165 }
166 }
167
168 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
169 if (mDisplayMagnifier != null) {
170 mDisplayMagnifier.onAppWindowTransitionLocked(windowState, transition);
171 }
172 // Not relevant for the window observer.
173 }
174
175 public void onWindowTransitionLocked(WindowState windowState, int transition) {
176 if (mDisplayMagnifier != null) {
177 mDisplayMagnifier.onWindowTransitionLocked(windowState, transition);
178 }
179 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700180 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800181 }
182 }
183
Svetoslav3a0d8782014-12-04 12:50:11 -0800184 public void onWindowFocusChangedNotLocked() {
Svetoslav8e3feb12014-02-24 13:46:47 -0800185 // Not relevant for the display magnifier.
186
Svetoslav3a0d8782014-12-04 12:50:11 -0800187 WindowsForAccessibilityObserver observer = null;
188 synchronized (mWindowManagerService) {
189 observer = mWindowsForAccessibilityObserver;
190 }
191 if (observer != null) {
192 observer.performComputeChangedWindowsNotLocked();
Svetoslav8e3feb12014-02-24 13:46:47 -0800193 }
194 }
195
Svetoslav4604abc2014-06-10 18:59:30 -0700196
Svetoslavf7174e82014-06-12 11:29:35 -0700197 public void onSomeWindowResizedOrMovedLocked() {
Svetoslav4604abc2014-06-10 18:59:30 -0700198 // Not relevant for the display magnifier.
199
200 if (mWindowsForAccessibilityObserver != null) {
Svetoslavf7174e82014-06-12 11:29:35 -0700201 mWindowsForAccessibilityObserver.scheduleComputeChangedWindowsLocked();
Svetoslav4604abc2014-06-10 18:59:30 -0700202 }
203 }
204
Svetoslav8e3feb12014-02-24 13:46:47 -0800205 /** NOTE: This has to be called within a surface transaction. */
206 public void drawMagnifiedRegionBorderIfNeededLocked() {
207 if (mDisplayMagnifier != null) {
208 mDisplayMagnifier.drawMagnifiedRegionBorderIfNeededLocked();
209 }
210 // Not relevant for the window observer.
211 }
212
213 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
214 if (mDisplayMagnifier != null) {
215 return mDisplayMagnifier.getMagnificationSpecForWindowLocked(windowState);
216 }
217 return null;
218 }
219
220 public boolean hasCallbacksLocked() {
221 return (mDisplayMagnifier != null
222 || mWindowsForAccessibilityObserver != null);
223 }
224
225 private static void populateTransformationMatrixLocked(WindowState windowState,
226 Matrix outMatrix) {
227 sTempFloats[Matrix.MSCALE_X] = windowState.mWinAnimator.mDsDx;
228 sTempFloats[Matrix.MSKEW_Y] = windowState.mWinAnimator.mDtDx;
Robert Carr0edf18f2017-02-21 20:01:47 -0800229 sTempFloats[Matrix.MSKEW_X] = windowState.mWinAnimator.mDtDy;
230 sTempFloats[Matrix.MSCALE_Y] = windowState.mWinAnimator.mDsDy;
Filip Gruszczynski2a6a2c22015-10-14 12:00:53 -0700231 sTempFloats[Matrix.MTRANS_X] = windowState.mShownPosition.x;
232 sTempFloats[Matrix.MTRANS_Y] = windowState.mShownPosition.y;
Svetoslav8e3feb12014-02-24 13:46:47 -0800233 sTempFloats[Matrix.MPERSP_0] = 0;
234 sTempFloats[Matrix.MPERSP_1] = 0;
235 sTempFloats[Matrix.MPERSP_2] = 1;
236 outMatrix.setValues(sTempFloats);
237 }
238
239 /**
240 * This class encapsulates the functionality related to display magnification.
241 */
242 private static final class DisplayMagnifier {
243
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800244 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ? "DisplayMagnifier" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800245
246 private static final boolean DEBUG_WINDOW_TRANSITIONS = false;
247 private static final boolean DEBUG_ROTATION = false;
248 private static final boolean DEBUG_LAYERS = false;
249 private static final boolean DEBUG_RECTANGLE_REQUESTED = false;
250 private static final boolean DEBUG_VIEWPORT_WINDOW = false;
251
252 private final Rect mTempRect1 = new Rect();
253 private final Rect mTempRect2 = new Rect();
254
255 private final Region mTempRegion1 = new Region();
256 private final Region mTempRegion2 = new Region();
257 private final Region mTempRegion3 = new Region();
258 private final Region mTempRegion4 = new Region();
259
260 private final Context mContext;
261 private final WindowManagerService mWindowManagerService;
262 private final MagnifiedViewport mMagnifedViewport;
263 private final Handler mHandler;
264
265 private final MagnificationCallbacks mCallbacks;
266
267 private final long mLongAnimationDuration;
268
269 public DisplayMagnifier(WindowManagerService windowManagerService,
270 MagnificationCallbacks callbacks) {
271 mContext = windowManagerService.mContext;
272 mWindowManagerService = windowManagerService;
273 mCallbacks = callbacks;
274 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
275 mMagnifedViewport = new MagnifiedViewport();
276 mLongAnimationDuration = mContext.getResources().getInteger(
277 com.android.internal.R.integer.config_longAnimTime);
278 }
279
280 public void setMagnificationSpecLocked(MagnificationSpec spec) {
281 mMagnifedViewport.updateMagnificationSpecLocked(spec);
282 mMagnifedViewport.recomputeBoundsLocked();
283 mWindowManagerService.scheduleAnimationLocked();
284 }
285
Svetoslavf7174e82014-06-12 11:29:35 -0700286 public void onRectangleOnScreenRequestedLocked(Rect rectangle) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800287 if (DEBUG_RECTANGLE_REQUESTED) {
288 Slog.i(LOG_TAG, "Rectangle on screen requested: " + rectangle);
289 }
290 if (!mMagnifedViewport.isMagnifyingLocked()) {
291 return;
292 }
293 Rect magnifiedRegionBounds = mTempRect2;
294 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(magnifiedRegionBounds);
295 if (magnifiedRegionBounds.contains(rectangle)) {
296 return;
297 }
298 SomeArgs args = SomeArgs.obtain();
299 args.argi1 = rectangle.left;
300 args.argi2 = rectangle.top;
301 args.argi3 = rectangle.right;
302 args.argi4 = rectangle.bottom;
303 mHandler.obtainMessage(MyHandler.MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED,
304 args).sendToTarget();
305 }
306
307 public void onWindowLayersChangedLocked() {
308 if (DEBUG_LAYERS) {
309 Slog.i(LOG_TAG, "Layers changed.");
310 }
311 mMagnifedViewport.recomputeBoundsLocked();
312 mWindowManagerService.scheduleAnimationLocked();
313 }
314
Andrii Kulian8ee72852017-03-10 10:36:45 -0800315 public void onRotationChangedLocked(DisplayContent displayContent) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800316 if (DEBUG_ROTATION) {
Andrii Kulian8ee72852017-03-10 10:36:45 -0800317 final int rotation = displayContent.getRotation();
318 Slog.i(LOG_TAG, "Rotation: " + Surface.rotationToString(rotation)
Svetoslav8e3feb12014-02-24 13:46:47 -0800319 + " displayId: " + displayContent.getDisplayId());
320 }
321 mMagnifedViewport.onRotationChangedLocked();
322 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_ROTATION_CHANGED);
323 }
324
325 public void onAppWindowTransitionLocked(WindowState windowState, int transition) {
326 if (DEBUG_WINDOW_TRANSITIONS) {
327 Slog.i(LOG_TAG, "Window transition: "
328 + AppTransition.appTransitionToString(transition)
329 + " displayId: " + windowState.getDisplayId());
330 }
331 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
332 if (magnifying) {
333 switch (transition) {
334 case AppTransition.TRANSIT_ACTIVITY_OPEN:
335 case AppTransition.TRANSIT_TASK_OPEN:
336 case AppTransition.TRANSIT_TASK_TO_FRONT:
337 case AppTransition.TRANSIT_WALLPAPER_OPEN:
338 case AppTransition.TRANSIT_WALLPAPER_CLOSE:
339 case AppTransition.TRANSIT_WALLPAPER_INTRA_OPEN: {
340 mHandler.sendEmptyMessage(MyHandler.MESSAGE_NOTIFY_USER_CONTEXT_CHANGED);
341 }
342 }
343 }
344 }
345
346 public void onWindowTransitionLocked(WindowState windowState, int transition) {
347 if (DEBUG_WINDOW_TRANSITIONS) {
348 Slog.i(LOG_TAG, "Window transition: "
349 + AppTransition.appTransitionToString(transition)
350 + " displayId: " + windowState.getDisplayId());
351 }
352 final boolean magnifying = mMagnifedViewport.isMagnifyingLocked();
353 final int type = windowState.mAttrs.type;
354 switch (transition) {
355 case WindowManagerPolicy.TRANSIT_ENTER:
356 case WindowManagerPolicy.TRANSIT_SHOW: {
357 if (!magnifying) {
358 break;
359 }
360 switch (type) {
361 case WindowManager.LayoutParams.TYPE_APPLICATION:
Chong Zhangfea963e2016-08-15 17:14:16 -0700362 case WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION:
Svetoslav8e3feb12014-02-24 13:46:47 -0800363 case WindowManager.LayoutParams.TYPE_APPLICATION_PANEL:
364 case WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA:
365 case WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL:
Wale Ogunwale0a4dc222015-04-14 12:58:42 -0700366 case WindowManager.LayoutParams.TYPE_APPLICATION_ABOVE_SUB_PANEL:
Svetoslav8e3feb12014-02-24 13:46:47 -0800367 case WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG:
368 case WindowManager.LayoutParams.TYPE_SEARCH_BAR:
369 case WindowManager.LayoutParams.TYPE_PHONE:
370 case WindowManager.LayoutParams.TYPE_SYSTEM_ALERT:
371 case WindowManager.LayoutParams.TYPE_TOAST:
372 case WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY:
Wale Ogunwale5cd907d2017-01-26 14:14:08 -0800373 case WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY:
Svetoslav8e3feb12014-02-24 13:46:47 -0800374 case WindowManager.LayoutParams.TYPE_PRIORITY_PHONE:
375 case WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG:
376 case WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG:
377 case WindowManager.LayoutParams.TYPE_SYSTEM_ERROR:
378 case WindowManager.LayoutParams.TYPE_VOLUME_OVERLAY:
Jason Monk8f7f3182015-11-18 16:35:14 -0500379 case WindowManager.LayoutParams.TYPE_QS_DIALOG:
Adrian Roos9a645132014-10-08 02:59:56 +0200380 case WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL: {
Svetoslav8e3feb12014-02-24 13:46:47 -0800381 Rect magnifiedRegionBounds = mTempRect2;
382 mMagnifedViewport.getMagnifiedFrameInContentCoordsLocked(
383 magnifiedRegionBounds);
384 Rect touchableRegionBounds = mTempRect1;
385 windowState.getTouchableRegion(mTempRegion1);
386 mTempRegion1.getBounds(touchableRegionBounds);
387 if (!magnifiedRegionBounds.intersect(touchableRegionBounds)) {
388 mCallbacks.onRectangleOnScreenRequested(
389 touchableRegionBounds.left,
390 touchableRegionBounds.top,
391 touchableRegionBounds.right,
392 touchableRegionBounds.bottom);
393 }
394 } break;
395 } break;
396 }
397 }
398 }
399
400 public MagnificationSpec getMagnificationSpecForWindowLocked(WindowState windowState) {
401 MagnificationSpec spec = mMagnifedViewport.getMagnificationSpecLocked();
402 if (spec != null && !spec.isNop()) {
403 WindowManagerPolicy policy = mWindowManagerService.mPolicy;
404 final int windowType = windowState.mAttrs.type;
Wale Ogunwale7ed4d372016-07-09 15:28:55 -0700405 if (!policy.isTopLevelWindow(windowType) && windowState.isChildWindow()
Svetoslav8e3feb12014-02-24 13:46:47 -0800406 && !policy.canMagnifyWindow(windowType)) {
407 return null;
408 }
409 if (!policy.canMagnifyWindow(windowState.mAttrs.type)) {
410 return null;
411 }
412 }
413 return spec;
414 }
415
Phil Weaver70439242016-03-10 15:15:49 -0800416 public void getMagnificationRegionLocked(Region outMagnificationRegion) {
417 mMagnifedViewport.getMagnificationRegionLocked(outMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400418 }
419
Svetoslav8e3feb12014-02-24 13:46:47 -0800420 public void destroyLocked() {
421 mMagnifedViewport.destroyWindow();
422 }
423
424 /** NOTE: This has to be called within a surface transaction. */
425 public void drawMagnifiedRegionBorderIfNeededLocked() {
426 mMagnifedViewport.drawWindowIfNeededLocked();
427 }
428
429 private final class MagnifiedViewport {
430
Svetoslav8e3feb12014-02-24 13:46:47 -0800431 private final SparseArray<WindowState> mTempWindowStates =
432 new SparseArray<WindowState>();
433
434 private final RectF mTempRectF = new RectF();
435
436 private final Point mTempPoint = new Point();
437
438 private final Matrix mTempMatrix = new Matrix();
439
Phil Weaver70439242016-03-10 15:15:49 -0800440 private final Region mMagnificationRegion = new Region();
441 private final Region mOldMagnificationRegion = new Region();
Svetoslav8e3feb12014-02-24 13:46:47 -0800442
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800443 private final Path mCircularPath;
444
Svetoslav8e3feb12014-02-24 13:46:47 -0800445 private final MagnificationSpec mMagnificationSpec = MagnificationSpec.obtain();
446
447 private final WindowManager mWindowManager;
448
Svetoslav7505e332014-08-22 12:14:28 -0700449 private final float mBorderWidth;
Svetoslav8e3feb12014-02-24 13:46:47 -0800450 private final int mHalfBorderWidth;
Svetoslav7505e332014-08-22 12:14:28 -0700451 private final int mDrawBorderInset;
Svetoslav8e3feb12014-02-24 13:46:47 -0800452
453 private final ViewportWindow mWindow;
454
455 private boolean mFullRedrawNeeded;
456
457 public MagnifiedViewport() {
458 mWindowManager = (WindowManager) mContext.getSystemService(Service.WINDOW_SERVICE);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800459 mBorderWidth = mContext.getResources().getDimension(
460 com.android.internal.R.dimen.accessibility_magnification_indicator_width);
Svetoslav7505e332014-08-22 12:14:28 -0700461 mHalfBorderWidth = (int) Math.ceil(mBorderWidth / 2);
462 mDrawBorderInset = (int) mBorderWidth / 2;
Svetoslav8e3feb12014-02-24 13:46:47 -0800463 mWindow = new ViewportWindow(mContext);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800464
Adam Powell01f280d2015-05-18 16:07:42 -0700465 if (mContext.getResources().getConfiguration().isScreenRound()) {
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800466 mCircularPath = new Path();
467 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
468 final int centerXY = mTempPoint.x / 2;
469 mCircularPath.addCircle(centerXY, centerXY, centerXY, Path.Direction.CW);
470 } else {
471 mCircularPath = null;
472 }
473
Svetoslav8e3feb12014-02-24 13:46:47 -0800474 recomputeBoundsLocked();
475 }
476
Phil Weaver70439242016-03-10 15:15:49 -0800477 public void getMagnificationRegionLocked(@NonNull Region outMagnificationRegion) {
478 outMagnificationRegion.set(mMagnificationRegion);
Alan Viverette59e53a12016-03-28 13:41:32 -0400479 }
480
Svetoslav8e3feb12014-02-24 13:46:47 -0800481 public void updateMagnificationSpecLocked(MagnificationSpec spec) {
482 if (spec != null) {
483 mMagnificationSpec.initialize(spec.scale, spec.offsetX, spec.offsetY);
484 } else {
485 mMagnificationSpec.clear();
486 }
487 // If this message is pending we are in a rotation animation and do not want
488 // to show the border. We will do so when the pending message is handled.
Svetoslav7505e332014-08-22 12:14:28 -0700489 if (!mHandler.hasMessages(
490 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED)) {
Svetoslav8e3feb12014-02-24 13:46:47 -0800491 setMagnifiedRegionBorderShownLocked(isMagnifyingLocked(), true);
492 }
493 }
494
495 public void recomputeBoundsLocked() {
496 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
497 final int screenWidth = mTempPoint.x;
498 final int screenHeight = mTempPoint.y;
499
Phil Weaver70439242016-03-10 15:15:49 -0800500 mMagnificationRegion.set(0, 0, 0, 0);
501 final Region availableBounds = mTempRegion1;
502 availableBounds.set(0, 0, screenWidth, screenHeight);
Svetoslav8e3feb12014-02-24 13:46:47 -0800503
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800504 if (mCircularPath != null) {
Phil Weaver70439242016-03-10 15:15:49 -0800505 availableBounds.setPath(mCircularPath, availableBounds);
Casey Burkhardtd29a1e42015-02-12 14:07:55 -0800506 }
507
Svetoslav8e3feb12014-02-24 13:46:47 -0800508 Region nonMagnifiedBounds = mTempRegion4;
Svet Ganovb21df802014-09-01 19:06:33 -0700509 nonMagnifiedBounds.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800510
511 SparseArray<WindowState> visibleWindows = mTempWindowStates;
512 visibleWindows.clear();
513 populateWindowsOnScreenLocked(visibleWindows);
514
515 final int visibleWindowCount = visibleWindows.size();
516 for (int i = visibleWindowCount - 1; i >= 0; i--) {
517 WindowState windowState = visibleWindows.valueAt(i);
518 if (windowState.mAttrs.type == WindowManager
519 .LayoutParams.TYPE_MAGNIFICATION_OVERLAY) {
520 continue;
521 }
522
Phil Weaver65c06702016-03-15 15:33:46 -0700523 // Consider the touchable portion of the window
Svetoslav8e3feb12014-02-24 13:46:47 -0800524 Matrix matrix = mTempMatrix;
525 populateTransformationMatrixLocked(windowState, matrix);
Phil Weaver65c06702016-03-15 15:33:46 -0700526 Region touchableRegion = mTempRegion3;
527 windowState.getTouchableRegion(touchableRegion);
528 Rect touchableFrame = mTempRect1;
529 touchableRegion.getBounds(touchableFrame);
Svetoslav8e3feb12014-02-24 13:46:47 -0800530 RectF windowFrame = mTempRectF;
Phil Weaver65c06702016-03-15 15:33:46 -0700531 windowFrame.set(touchableFrame);
532 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
533 matrix.mapRect(windowFrame);
534 Region windowBounds = mTempRegion2;
535 windowBounds.set((int) windowFrame.left, (int) windowFrame.top,
536 (int) windowFrame.right, (int) windowFrame.bottom);
537 // Only update new regions
538 Region portionOfWindowAlreadyAccountedFor = mTempRegion3;
Phil Weaver70439242016-03-10 15:15:49 -0800539 portionOfWindowAlreadyAccountedFor.set(mMagnificationRegion);
Phil Weaver65c06702016-03-15 15:33:46 -0700540 portionOfWindowAlreadyAccountedFor.op(nonMagnifiedBounds, Region.Op.UNION);
541 windowBounds.op(portionOfWindowAlreadyAccountedFor, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800542
543 if (mWindowManagerService.mPolicy.canMagnifyWindow(windowState.mAttrs.type)) {
Phil Weaver70439242016-03-10 15:15:49 -0800544 mMagnificationRegion.op(windowBounds, Region.Op.UNION);
545 mMagnificationRegion.op(availableBounds, Region.Op.INTERSECT);
Svetoslav8e3feb12014-02-24 13:46:47 -0800546 } else {
Svetoslav8e3feb12014-02-24 13:46:47 -0800547 nonMagnifiedBounds.op(windowBounds, Region.Op.UNION);
Phil Weaver70439242016-03-10 15:15:49 -0800548 availableBounds.op(windowBounds, Region.Op.DIFFERENCE);
Svetoslav8e3feb12014-02-24 13:46:47 -0800549 }
550
Phil Weaver65c06702016-03-15 15:33:46 -0700551 // Update accounted bounds
Svetoslav8e3feb12014-02-24 13:46:47 -0800552 Region accountedBounds = mTempRegion2;
Phil Weaver70439242016-03-10 15:15:49 -0800553 accountedBounds.set(mMagnificationRegion);
Svetoslav8e3feb12014-02-24 13:46:47 -0800554 accountedBounds.op(nonMagnifiedBounds, Region.Op.UNION);
555 accountedBounds.op(0, 0, screenWidth, screenHeight, Region.Op.INTERSECT);
556
557 if (accountedBounds.isRect()) {
558 Rect accountedFrame = mTempRect1;
559 accountedBounds.getBounds(accountedFrame);
560 if (accountedFrame.width() == screenWidth
561 && accountedFrame.height() == screenHeight) {
562 break;
563 }
564 }
565 }
566
567 visibleWindows.clear();
568
Phil Weaver70439242016-03-10 15:15:49 -0800569 mMagnificationRegion.op(mDrawBorderInset, mDrawBorderInset,
Svetoslav7505e332014-08-22 12:14:28 -0700570 screenWidth - mDrawBorderInset, screenHeight - mDrawBorderInset,
Svetoslav8e3feb12014-02-24 13:46:47 -0800571 Region.Op.INTERSECT);
572
Phil Weaver70439242016-03-10 15:15:49 -0800573 final boolean magnifiedChanged =
574 !mOldMagnificationRegion.equals(mMagnificationRegion);
575 if (magnifiedChanged) {
576 mWindow.setBounds(mMagnificationRegion);
577 final Rect dirtyRect = mTempRect1;
578 if (mFullRedrawNeeded) {
579 mFullRedrawNeeded = false;
580 dirtyRect.set(mDrawBorderInset, mDrawBorderInset,
581 screenWidth - mDrawBorderInset,
582 screenHeight - mDrawBorderInset);
583 mWindow.invalidate(dirtyRect);
584 } else {
585 final Region dirtyRegion = mTempRegion3;
586 dirtyRegion.set(mMagnificationRegion);
587 dirtyRegion.op(mOldMagnificationRegion, Region.Op.UNION);
588 dirtyRegion.op(nonMagnifiedBounds, Region.Op.INTERSECT);
589 dirtyRegion.getBounds(dirtyRect);
590 mWindow.invalidate(dirtyRect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800591 }
592
Phil Weaver70439242016-03-10 15:15:49 -0800593 mOldMagnificationRegion.set(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500594 final SomeArgs args = SomeArgs.obtain();
Phil Weaver70439242016-03-10 15:15:49 -0800595 args.arg1 = Region.obtain(mMagnificationRegion);
Alan Viverette214fb682015-11-17 09:47:11 -0500596 mHandler.obtainMessage(
Phil Weaver70439242016-03-10 15:15:49 -0800597 MyHandler.MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED, args)
598 .sendToTarget();
Svetoslav8e3feb12014-02-24 13:46:47 -0800599 }
600 }
601
602 public void onRotationChangedLocked() {
603 // If we are magnifying, hide the magnified border window immediately so
604 // the user does not see strange artifacts during rotation. The screenshot
605 // used for rotation has already the border. After the rotation is complete
606 // we will show the border.
607 if (isMagnifyingLocked()) {
608 setMagnifiedRegionBorderShownLocked(false, false);
609 final long delay = (long) (mLongAnimationDuration
Dianne Hackborneb94fa72014-06-03 17:48:12 -0700610 * mWindowManagerService.getWindowAnimationScaleLocked());
Svetoslav8e3feb12014-02-24 13:46:47 -0800611 Message message = mHandler.obtainMessage(
612 MyHandler.MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED);
613 mHandler.sendMessageDelayed(message, delay);
614 }
615 recomputeBoundsLocked();
616 mWindow.updateSize();
617 }
618
619 public void setMagnifiedRegionBorderShownLocked(boolean shown, boolean animate) {
620 if (shown) {
621 mFullRedrawNeeded = true;
Phil Weaver70439242016-03-10 15:15:49 -0800622 mOldMagnificationRegion.set(0, 0, 0, 0);
Svetoslav8e3feb12014-02-24 13:46:47 -0800623 }
624 mWindow.setShown(shown, animate);
625 }
626
627 public void getMagnifiedFrameInContentCoordsLocked(Rect rect) {
628 MagnificationSpec spec = mMagnificationSpec;
Phil Weaver70439242016-03-10 15:15:49 -0800629 mMagnificationRegion.getBounds(rect);
Svetoslav8e3feb12014-02-24 13:46:47 -0800630 rect.offset((int) -spec.offsetX, (int) -spec.offsetY);
631 rect.scale(1.0f / spec.scale);
632 }
633
634 public boolean isMagnifyingLocked() {
635 return mMagnificationSpec.scale > 1.0f;
636 }
637
638 public MagnificationSpec getMagnificationSpecLocked() {
639 return mMagnificationSpec;
640 }
641
642 /** NOTE: This has to be called within a surface transaction. */
643 public void drawWindowIfNeededLocked() {
644 recomputeBoundsLocked();
645 mWindow.drawIfNeeded();
646 }
647
648 public void destroyWindow() {
649 mWindow.releaseSurface();
650 }
651
652 private void populateWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Wale Ogunwalef7cab102016-10-25 15:25:14 -0700653 final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
Wale Ogunwaled1880962016-11-08 10:31:59 -0800654 dc.forAllWindows((w) -> {
655 if (w.isOnScreen() && w.isVisibleLw()
656 && !w.mWinAnimator.mEnterAnimationPending) {
657 outWindows.put(w.mLayer, w);
Svetoslav8e3feb12014-02-24 13:46:47 -0800658 }
Wale Ogunwaled1880962016-11-08 10:31:59 -0800659 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -0800660 }
661
662 private final class ViewportWindow {
663 private static final String SURFACE_TITLE = "Magnification Overlay";
664
Svetoslav8e3feb12014-02-24 13:46:47 -0800665 private final Region mBounds = new Region();
666 private final Rect mDirtyRect = new Rect();
667 private final Paint mPaint = new Paint();
668
Svetoslav8e3feb12014-02-24 13:46:47 -0800669 private final SurfaceControl mSurfaceControl;
670 private final Surface mSurface = new Surface();
671
Svet Ganovb21df802014-09-01 19:06:33 -0700672 private final AnimationController mAnimationController;
673
Svetoslav8e3feb12014-02-24 13:46:47 -0800674 private boolean mShown;
675 private int mAlpha;
676
677 private boolean mInvalidated;
678
679 public ViewportWindow(Context context) {
680 SurfaceControl surfaceControl = null;
681 try {
682 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
683 surfaceControl = new SurfaceControl(mWindowManagerService.mFxSession,
684 SURFACE_TITLE, mTempPoint.x, mTempPoint.y, PixelFormat.TRANSLUCENT,
685 SurfaceControl.HIDDEN);
686 } catch (OutOfResourcesException oore) {
687 /* ignore */
688 }
689 mSurfaceControl = surfaceControl;
690 mSurfaceControl.setLayerStack(mWindowManager.getDefaultDisplay()
691 .getLayerStack());
Wale Ogunwale5cd907d2017-01-26 14:14:08 -0800692 mSurfaceControl.setLayer(mWindowManagerService.mPolicy.getWindowLayerFromTypeLw(
Svetoslav8e3feb12014-02-24 13:46:47 -0800693 WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY)
694 * WindowManagerService.TYPE_LAYER_MULTIPLIER);
695 mSurfaceControl.setPosition(0, 0);
696 mSurface.copyFrom(mSurfaceControl);
697
Svet Ganovb21df802014-09-01 19:06:33 -0700698 mAnimationController = new AnimationController(context,
699 mWindowManagerService.mH.getLooper());
700
Svetoslav8e3feb12014-02-24 13:46:47 -0800701 TypedValue typedValue = new TypedValue();
702 context.getTheme().resolveAttribute(R.attr.colorActivatedHighlight,
703 typedValue, true);
Alan Viverette4a357cd2015-03-18 18:37:18 -0700704 final int borderColor = context.getColor(typedValue.resourceId);
Svetoslav8e3feb12014-02-24 13:46:47 -0800705
706 mPaint.setStyle(Paint.Style.STROKE);
707 mPaint.setStrokeWidth(mBorderWidth);
708 mPaint.setColor(borderColor);
709
Svetoslav8e3feb12014-02-24 13:46:47 -0800710 mInvalidated = true;
711 }
712
713 public void setShown(boolean shown, boolean animate) {
714 synchronized (mWindowManagerService.mWindowMap) {
715 if (mShown == shown) {
716 return;
717 }
718 mShown = shown;
Svet Ganovb21df802014-09-01 19:06:33 -0700719 mAnimationController.onFrameShownStateChanged(shown, animate);
Svetoslav8e3feb12014-02-24 13:46:47 -0800720 if (DEBUG_VIEWPORT_WINDOW) {
721 Slog.i(LOG_TAG, "ViewportWindow shown: " + mShown);
722 }
723 }
724 }
725
726 @SuppressWarnings("unused")
727 // Called reflectively from an animator.
728 public int getAlpha() {
729 synchronized (mWindowManagerService.mWindowMap) {
730 return mAlpha;
731 }
732 }
733
734 public void setAlpha(int alpha) {
735 synchronized (mWindowManagerService.mWindowMap) {
736 if (mAlpha == alpha) {
737 return;
738 }
739 mAlpha = alpha;
740 invalidate(null);
741 if (DEBUG_VIEWPORT_WINDOW) {
742 Slog.i(LOG_TAG, "ViewportWindow set alpha: " + alpha);
743 }
744 }
745 }
746
747 public void setBounds(Region bounds) {
748 synchronized (mWindowManagerService.mWindowMap) {
749 if (mBounds.equals(bounds)) {
750 return;
751 }
752 mBounds.set(bounds);
753 invalidate(mDirtyRect);
754 if (DEBUG_VIEWPORT_WINDOW) {
755 Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
756 }
757 }
758 }
759
760 public void updateSize() {
761 synchronized (mWindowManagerService.mWindowMap) {
762 mWindowManager.getDefaultDisplay().getRealSize(mTempPoint);
763 mSurfaceControl.setSize(mTempPoint.x, mTempPoint.y);
764 invalidate(mDirtyRect);
765 }
766 }
767
768 public void invalidate(Rect dirtyRect) {
769 if (dirtyRect != null) {
770 mDirtyRect.set(dirtyRect);
771 } else {
772 mDirtyRect.setEmpty();
773 }
774 mInvalidated = true;
775 mWindowManagerService.scheduleAnimationLocked();
776 }
777
778 /** NOTE: This has to be called within a surface transaction. */
779 public void drawIfNeeded() {
780 synchronized (mWindowManagerService.mWindowMap) {
781 if (!mInvalidated) {
782 return;
783 }
784 mInvalidated = false;
785 Canvas canvas = null;
786 try {
787 // Empty dirty rectangle means unspecified.
788 if (mDirtyRect.isEmpty()) {
789 mBounds.getBounds(mDirtyRect);
790 }
791 mDirtyRect.inset(- mHalfBorderWidth, - mHalfBorderWidth);
792 canvas = mSurface.lockCanvas(mDirtyRect);
793 if (DEBUG_VIEWPORT_WINDOW) {
794 Slog.i(LOG_TAG, "Dirty rect: " + mDirtyRect);
795 }
796 } catch (IllegalArgumentException iae) {
797 /* ignore */
798 } catch (Surface.OutOfResourcesException oore) {
799 /* ignore */
800 }
801 if (canvas == null) {
802 return;
803 }
804 if (DEBUG_VIEWPORT_WINDOW) {
805 Slog.i(LOG_TAG, "Bounds: " + mBounds);
806 }
807 canvas.drawColor(Color.TRANSPARENT, Mode.CLEAR);
808 mPaint.setAlpha(mAlpha);
809 Path path = mBounds.getBoundaryPath();
810 canvas.drawPath(path, mPaint);
811
812 mSurface.unlockCanvasAndPost(canvas);
813
814 if (mAlpha > 0) {
815 mSurfaceControl.show();
816 } else {
817 mSurfaceControl.hide();
818 }
819 }
820 }
821
822 public void releaseSurface() {
823 mSurfaceControl.release();
824 mSurface.release();
825 }
Svet Ganovb21df802014-09-01 19:06:33 -0700826
827 private final class AnimationController extends Handler {
828 private static final String PROPERTY_NAME_ALPHA = "alpha";
829
830 private static final int MIN_ALPHA = 0;
831 private static final int MAX_ALPHA = 255;
832
833 private static final int MSG_FRAME_SHOWN_STATE_CHANGED = 1;
834
835 private final ValueAnimator mShowHideFrameAnimator;
836
837 public AnimationController(Context context, Looper looper) {
838 super(looper);
839 mShowHideFrameAnimator = ObjectAnimator.ofInt(ViewportWindow.this,
840 PROPERTY_NAME_ALPHA, MIN_ALPHA, MAX_ALPHA);
841
842 Interpolator interpolator = new DecelerateInterpolator(2.5f);
843 final long longAnimationDuration = context.getResources().getInteger(
844 com.android.internal.R.integer.config_longAnimTime);
845
846 mShowHideFrameAnimator.setInterpolator(interpolator);
847 mShowHideFrameAnimator.setDuration(longAnimationDuration);
848 }
849
850 public void onFrameShownStateChanged(boolean shown, boolean animate) {
851 obtainMessage(MSG_FRAME_SHOWN_STATE_CHANGED,
852 shown ? 1 : 0, animate ? 1 : 0).sendToTarget();
853 }
854
855 @Override
856 public void handleMessage(Message message) {
857 switch (message.what) {
858 case MSG_FRAME_SHOWN_STATE_CHANGED: {
859 final boolean shown = message.arg1 == 1;
860 final boolean animate = message.arg2 == 1;
861
862 if (animate) {
863 if (mShowHideFrameAnimator.isRunning()) {
864 mShowHideFrameAnimator.reverse();
865 } else {
866 if (shown) {
867 mShowHideFrameAnimator.start();
868 } else {
869 mShowHideFrameAnimator.reverse();
870 }
871 }
872 } else {
873 mShowHideFrameAnimator.cancel();
874 if (shown) {
875 setAlpha(MAX_ALPHA);
876 } else {
877 setAlpha(MIN_ALPHA);
878 }
879 }
880 } break;
881 }
882 }
883 }
Svetoslav8e3feb12014-02-24 13:46:47 -0800884 }
885 }
886
887 private class MyHandler extends Handler {
Phil Weaver70439242016-03-10 15:15:49 -0800888 public static final int MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -0800889 public static final int MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED = 2;
890 public static final int MESSAGE_NOTIFY_USER_CONTEXT_CHANGED = 3;
891 public static final int MESSAGE_NOTIFY_ROTATION_CHANGED = 4;
892 public static final int MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED = 5;
893
894 public MyHandler(Looper looper) {
895 super(looper);
896 }
897
898 @Override
899 public void handleMessage(Message message) {
900 switch (message.what) {
Phil Weaver70439242016-03-10 15:15:49 -0800901 case MESSAGE_NOTIFY_MAGNIFICATION_REGION_CHANGED: {
Alan Viverette214fb682015-11-17 09:47:11 -0500902 final SomeArgs args = (SomeArgs) message.obj;
903 final Region magnifiedBounds = (Region) args.arg1;
Phil Weaver70439242016-03-10 15:15:49 -0800904 mCallbacks.onMagnificationRegionChanged(magnifiedBounds);
Alan Viverette214fb682015-11-17 09:47:11 -0500905 magnifiedBounds.recycle();
Svetoslav8e3feb12014-02-24 13:46:47 -0800906 } break;
907
908 case MESSAGE_NOTIFY_RECTANGLE_ON_SCREEN_REQUESTED: {
909 SomeArgs args = (SomeArgs) message.obj;
910 final int left = args.argi1;
911 final int top = args.argi2;
912 final int right = args.argi3;
913 final int bottom = args.argi4;
914 mCallbacks.onRectangleOnScreenRequested(left, top, right, bottom);
915 args.recycle();
916 } break;
917
918 case MESSAGE_NOTIFY_USER_CONTEXT_CHANGED: {
919 mCallbacks.onUserContextChanged();
920 } break;
921
922 case MESSAGE_NOTIFY_ROTATION_CHANGED: {
923 final int rotation = message.arg1;
924 mCallbacks.onRotationChanged(rotation);
925 } break;
926
927 case MESSAGE_SHOW_MAGNIFIED_REGION_BOUNDS_IF_NEEDED : {
928 synchronized (mWindowManagerService.mWindowMap) {
929 if (mMagnifedViewport.isMagnifyingLocked()) {
930 mMagnifedViewport.setMagnifiedRegionBorderShownLocked(true, true);
931 mWindowManagerService.scheduleAnimationLocked();
932 }
933 }
934 } break;
935 }
936 }
937 }
938 }
939
940 /**
941 * This class encapsulates the functionality related to computing the windows
942 * reported for accessibility purposes. These windows are all windows a sighted
943 * user can see on the screen.
944 */
945 private static final class WindowsForAccessibilityObserver {
Filip Gruszczynski0bd180d2015-12-07 15:43:52 -0800946 private static final String LOG_TAG = TAG_WITH_CLASS_NAME ?
947 "WindowsForAccessibilityObserver" : TAG_WM;
Svetoslav8e3feb12014-02-24 13:46:47 -0800948
949 private static final boolean DEBUG = false;
950
951 private final SparseArray<WindowState> mTempWindowStates =
952 new SparseArray<WindowState>();
953
954 private final List<WindowInfo> mOldWindows = new ArrayList<WindowInfo>();
955
956 private final Set<IBinder> mTempBinderSet = new ArraySet<IBinder>();
957
958 private final RectF mTempRectF = new RectF();
959
960 private final Matrix mTempMatrix = new Matrix();
961
962 private final Point mTempPoint = new Point();
963
964 private final Rect mTempRect = new Rect();
965
966 private final Region mTempRegion = new Region();
967
968 private final Region mTempRegion1 = new Region();
969
970 private final Context mContext;
971
972 private final WindowManagerService mWindowManagerService;
973
974 private final Handler mHandler;
975
976 private final WindowsForAccessibilityCallback mCallback;
977
Svetoslavf7174e82014-06-12 11:29:35 -0700978 private final long mRecurringAccessibilityEventsIntervalMillis;
979
Svetoslav8e3feb12014-02-24 13:46:47 -0800980 public WindowsForAccessibilityObserver(WindowManagerService windowManagerService,
981 WindowsForAccessibilityCallback callback) {
982 mContext = windowManagerService.mContext;
983 mWindowManagerService = windowManagerService;
984 mCallback = callback;
985 mHandler = new MyHandler(mWindowManagerService.mH.getLooper());
Svetoslavf7174e82014-06-12 11:29:35 -0700986 mRecurringAccessibilityEventsIntervalMillis = ViewConfiguration
987 .getSendRecurringAccessibilityEventsInterval();
Svetoslav8e3feb12014-02-24 13:46:47 -0800988 computeChangedWindows();
989 }
990
Svetoslav3a0d8782014-12-04 12:50:11 -0800991 public void performComputeChangedWindowsNotLocked() {
992 mHandler.removeMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS);
993 computeChangedWindows();
994 }
995
Svetoslavf7174e82014-06-12 11:29:35 -0700996 public void scheduleComputeChangedWindowsLocked() {
Svetoslav3a0d8782014-12-04 12:50:11 -0800997 if (!mHandler.hasMessages(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS)) {
Svetoslavf7174e82014-06-12 11:29:35 -0700998 mHandler.sendEmptyMessageDelayed(MyHandler.MESSAGE_COMPUTE_CHANGED_WINDOWS,
999 mRecurringAccessibilityEventsIntervalMillis);
1000 }
1001 }
1002
Svetoslav8e3feb12014-02-24 13:46:47 -08001003 public void computeChangedWindows() {
1004 if (DEBUG) {
1005 Slog.i(LOG_TAG, "computeChangedWindows()");
1006 }
1007
Svetoslav3a0d8782014-12-04 12:50:11 -08001008 boolean windowsChanged = false;
1009 List<WindowInfo> windows = new ArrayList<WindowInfo>();
1010
Svetoslav8e3feb12014-02-24 13:46:47 -08001011 synchronized (mWindowManagerService.mWindowMap) {
Svetoslavf7174e82014-06-12 11:29:35 -07001012 // Do not send the windows if there is no current focus as
1013 // the window manager is still looking for where to put it.
1014 // We will do the work when we get a focus change callback.
1015 if (mWindowManagerService.mCurrentFocus == null) {
1016 return;
1017 }
1018
Svetoslav8e3feb12014-02-24 13:46:47 -08001019 WindowManager windowManager = (WindowManager)
1020 mContext.getSystemService(Context.WINDOW_SERVICE);
1021 windowManager.getDefaultDisplay().getRealSize(mTempPoint);
1022 final int screenWidth = mTempPoint.x;
1023 final int screenHeight = mTempPoint.y;
1024
1025 Region unaccountedSpace = mTempRegion;
1026 unaccountedSpace.set(0, 0, screenWidth, screenHeight);
1027
Wale Ogunwalef7cab102016-10-25 15:25:14 -07001028 final SparseArray<WindowState> visibleWindows = mTempWindowStates;
Svetoslav8e3feb12014-02-24 13:46:47 -08001029 populateVisibleWindowsOnScreenLocked(visibleWindows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001030 Set<IBinder> addedWindows = mTempBinderSet;
1031 addedWindows.clear();
1032
Svetoslavf7174e82014-06-12 11:29:35 -07001033 boolean focusedWindowAdded = false;
1034
Svetoslav8e3feb12014-02-24 13:46:47 -08001035 final int visibleWindowCount = visibleWindows.size();
Allen Hairf20ac2c2016-02-11 17:42:59 -08001036 HashSet<Integer> skipRemainingWindowsForTasks = new HashSet<>();
Svetoslav8e3feb12014-02-24 13:46:47 -08001037 for (int i = visibleWindowCount - 1; i >= 0; i--) {
Alan Viverette9538eea2014-11-13 14:49:20 -08001038 final WindowState windowState = visibleWindows.valueAt(i);
Svetoslav8e3feb12014-02-24 13:46:47 -08001039 final int flags = windowState.mAttrs.flags;
Allen Hairf20ac2c2016-02-11 17:42:59 -08001040 final Task task = windowState.getTask();
1041
1042 // If the window is part of a task that we're finished with - ignore.
1043 if (task != null && skipRemainingWindowsForTasks.contains(task.mTaskId)) {
1044 continue;
1045 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001046
Svetoslav3a5c7212014-10-14 09:54:26 -07001047 // If the window is not touchable - ignore.
Svetoslavf7174e82014-06-12 11:29:35 -07001048 if ((flags & WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE) != 0) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001049 continue;
1050 }
1051
Alan Viverette9538eea2014-11-13 14:49:20 -08001052 // Compute the bounds in the screen.
1053 final Rect boundsInScreen = mTempRect;
1054 computeWindowBoundsInScreen(windowState, boundsInScreen);
1055
Svetoslav8e3feb12014-02-24 13:46:47 -08001056 // If the window is completely covered by other windows - ignore.
1057 if (unaccountedSpace.quickReject(boundsInScreen)) {
1058 continue;
1059 }
1060
1061 // Add windows of certain types not covered by modal windows.
1062 if (isReportedWindowType(windowState.mAttrs.type)) {
1063 // Add the window to the ones to be reported.
Svetoslavf7174e82014-06-12 11:29:35 -07001064 WindowInfo window = obtainPopulatedWindowInfo(windowState, boundsInScreen);
Svetoslav8e3feb12014-02-24 13:46:47 -08001065 addedWindows.add(window.token);
Svetoslav8e3feb12014-02-24 13:46:47 -08001066 windows.add(window);
Svetoslavf7174e82014-06-12 11:29:35 -07001067 if (windowState.isFocused()) {
1068 focusedWindowAdded = true;
1069 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001070 }
1071
Alan Viveretted0c73f42014-11-18 10:25:04 -08001072 // Account for the space this window takes if the window
1073 // is not an accessibility overlay which does not change
1074 // the reported windows.
1075 if (windowState.mAttrs.type !=
1076 WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY) {
1077 unaccountedSpace.op(boundsInScreen, unaccountedSpace,
1078 Region.Op.REVERSE_DIFFERENCE);
1079 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001080
Allen Hairf20ac2c2016-02-11 17:42:59 -08001081 // If a window is modal it prevents other windows from being touched
Svetoslav8e3feb12014-02-24 13:46:47 -08001082 if ((flags & (WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
1083 | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL)) == 0) {
Phil Weaverac9ad702016-07-27 18:03:10 -07001084 // Account for all space in the task, whether the windows in it are
1085 // touchable or not. The modal window blocks all touches from the task's
1086 // area.
1087 unaccountedSpace.op(windowState.getDisplayFrameLw(), unaccountedSpace,
1088 Region.Op.REVERSE_DIFFERENCE);
1089
Allen Hairf20ac2c2016-02-11 17:42:59 -08001090 if (task != null) {
1091 // If the window is associated with a particular task, we can skip the
1092 // rest of the windows for that task.
1093 skipRemainingWindowsForTasks.add(task.mTaskId);
1094 continue;
1095 } else {
1096 // If the window is not associated with a particular task, then it is
1097 // globally modal. In this case we can skip all remaining windows.
1098 break;
1099 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001100 }
Phil Weaverac9ad702016-07-27 18:03:10 -07001101 // We figured out what is touchable for the entire screen - done.
1102 if (unaccountedSpace.isEmpty()) {
1103 break;
1104 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001105 }
1106
Svetoslavf7174e82014-06-12 11:29:35 -07001107 // Always report the focused window.
1108 if (!focusedWindowAdded) {
1109 for (int i = visibleWindowCount - 1; i >= 0; i--) {
1110 WindowState windowState = visibleWindows.valueAt(i);
1111 if (windowState.isFocused()) {
1112 // Compute the bounds in the screen.
1113 Rect boundsInScreen = mTempRect;
1114 computeWindowBoundsInScreen(windowState, boundsInScreen);
1115
1116 // Add the window to the ones to be reported.
1117 WindowInfo window = obtainPopulatedWindowInfo(windowState,
1118 boundsInScreen);
1119 addedWindows.add(window.token);
1120 windows.add(window);
1121 break;
1122 }
1123 }
1124 }
1125
Svetoslav8e3feb12014-02-24 13:46:47 -08001126 // Remove child/parent references to windows that were not added.
1127 final int windowCount = windows.size();
1128 for (int i = 0; i < windowCount; i++) {
1129 WindowInfo window = windows.get(i);
1130 if (!addedWindows.contains(window.parentToken)) {
1131 window.parentToken = null;
1132 }
1133 if (window.childTokens != null) {
1134 final int childTokenCount = window.childTokens.size();
1135 for (int j = childTokenCount - 1; j >= 0; j--) {
1136 if (!addedWindows.contains(window.childTokens.get(j))) {
1137 window.childTokens.remove(j);
1138 }
1139 }
1140 // Leave the child token list if empty.
1141 }
1142 }
1143
1144 visibleWindows.clear();
1145 addedWindows.clear();
1146
1147 // We computed the windows and if they changed notify the client.
Svetoslav8e3feb12014-02-24 13:46:47 -08001148 if (mOldWindows.size() != windows.size()) {
1149 // Different size means something changed.
1150 windowsChanged = true;
1151 } else if (!mOldWindows.isEmpty() || !windows.isEmpty()) {
1152 // Since we always traverse windows from high to low layer
1153 // the old and new windows at the same index should be the
1154 // same, otherwise something changed.
1155 for (int i = 0; i < windowCount; i++) {
1156 WindowInfo oldWindow = mOldWindows.get(i);
1157 WindowInfo newWindow = windows.get(i);
1158 // We do not care for layer changes given the window
1159 // order does not change. This brings no new information
1160 // to the clients.
1161 if (windowChangedNoLayer(oldWindow, newWindow)) {
1162 windowsChanged = true;
1163 break;
1164 }
1165 }
1166 }
1167
1168 if (windowsChanged) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001169 cacheWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001170 }
1171 }
Svetoslav3a0d8782014-12-04 12:50:11 -08001172
1173 // Now we do not hold the lock, so send the windows over.
1174 if (windowsChanged) {
1175 if (DEBUG) {
1176 Log.i(LOG_TAG, "Windows changed:" + windows);
1177 }
1178 mCallback.onWindowsForAccessibilityChanged(windows);
1179 } else {
1180 if (DEBUG) {
1181 Log.i(LOG_TAG, "No windows changed.");
1182 }
1183 }
1184
1185 // Recycle the windows as we do not need them.
1186 clearAndRecycleWindows(windows);
Svetoslav8e3feb12014-02-24 13:46:47 -08001187 }
1188
Svetoslavf7174e82014-06-12 11:29:35 -07001189 private void computeWindowBoundsInScreen(WindowState windowState, Rect outBounds) {
1190 // Get the touchable frame.
1191 Region touchableRegion = mTempRegion1;
1192 windowState.getTouchableRegion(touchableRegion);
1193 Rect touchableFrame = mTempRect;
1194 touchableRegion.getBounds(touchableFrame);
1195
1196 // Move to origin as all transforms are captured by the matrix.
1197 RectF windowFrame = mTempRectF;
1198 windowFrame.set(touchableFrame);
1199 windowFrame.offset(-windowState.mFrame.left, -windowState.mFrame.top);
1200
1201 // Map the frame to get what appears on the screen.
1202 Matrix matrix = mTempMatrix;
1203 populateTransformationMatrixLocked(windowState, matrix);
1204 matrix.mapRect(windowFrame);
1205
1206 // Got the bounds.
1207 outBounds.set((int) windowFrame.left, (int) windowFrame.top,
1208 (int) windowFrame.right, (int) windowFrame.bottom);
1209 }
1210
Wale Ogunwaleadde52e2016-07-16 13:11:55 -07001211 private static WindowInfo obtainPopulatedWindowInfo(
1212 WindowState windowState, Rect boundsInScreen) {
1213 final WindowInfo window = windowState.getWindowInfo();
Svetoslavf7174e82014-06-12 11:29:35 -07001214 window.boundsInScreen.set(boundsInScreen);
Svetoslavf7174e82014-06-12 11:29:35 -07001215 return window;
1216 }
1217
Svetoslav8e3feb12014-02-24 13:46:47 -08001218 private void cacheWindows(List<WindowInfo> windows) {
1219 final int oldWindowCount = mOldWindows.size();
1220 for (int i = oldWindowCount - 1; i >= 0; i--) {
1221 mOldWindows.remove(i).recycle();
1222 }
1223 final int newWindowCount = windows.size();
1224 for (int i = 0; i < newWindowCount; i++) {
1225 WindowInfo newWindow = windows.get(i);
1226 mOldWindows.add(WindowInfo.obtain(newWindow));
1227 }
1228 }
1229
1230 private boolean windowChangedNoLayer(WindowInfo oldWindow, WindowInfo newWindow) {
1231 if (oldWindow == newWindow) {
1232 return false;
1233 }
Svetoslavf7174e82014-06-12 11:29:35 -07001234 if (oldWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001235 return true;
1236 }
Svetoslavf7174e82014-06-12 11:29:35 -07001237 if (newWindow == null) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001238 return true;
1239 }
1240 if (oldWindow.type != newWindow.type) {
1241 return true;
1242 }
1243 if (oldWindow.focused != newWindow.focused) {
1244 return true;
1245 }
1246 if (oldWindow.token == null) {
1247 if (newWindow.token != null) {
1248 return true;
1249 }
1250 } else if (!oldWindow.token.equals(newWindow.token)) {
1251 return true;
1252 }
1253 if (oldWindow.parentToken == null) {
1254 if (newWindow.parentToken != null) {
1255 return true;
1256 }
1257 } else if (!oldWindow.parentToken.equals(newWindow.parentToken)) {
1258 return true;
1259 }
1260 if (!oldWindow.boundsInScreen.equals(newWindow.boundsInScreen)) {
1261 return true;
1262 }
1263 if (oldWindow.childTokens != null && newWindow.childTokens != null
1264 && !oldWindow.childTokens.equals(newWindow.childTokens)) {
1265 return true;
1266 }
Phil Weaver396d5492016-03-22 17:53:50 -07001267 if (!TextUtils.equals(oldWindow.title, newWindow.title)) {
1268 return true;
1269 }
1270 if (oldWindow.accessibilityIdOfAnchor != newWindow.accessibilityIdOfAnchor) {
1271 return true;
1272 }
Svetoslav8e3feb12014-02-24 13:46:47 -08001273 return false;
1274 }
1275
Svetoslav3a0d8782014-12-04 12:50:11 -08001276 private static void clearAndRecycleWindows(List<WindowInfo> windows) {
Svetoslav8e3feb12014-02-24 13:46:47 -08001277 final int windowCount = windows.size();
1278 for (int i = windowCount - 1; i >= 0; i--) {
1279 windows.remove(i).recycle();
1280 }
1281 }
1282
1283 private static boolean isReportedWindowType(int windowType) {
Jorim Jaggi73294b62016-10-26 18:02:36 -07001284 return (windowType != WindowManager.LayoutParams.TYPE_WALLPAPER
Svetoslav8e3feb12014-02-24 13:46:47 -08001285 && windowType != WindowManager.LayoutParams.TYPE_BOOT_PROGRESS
1286 && windowType != WindowManager.LayoutParams.TYPE_DISPLAY_OVERLAY
1287 && windowType != WindowManager.LayoutParams.TYPE_DRAG
Selim Cinekf83e8242015-05-19 18:08:14 -07001288 && windowType != WindowManager.LayoutParams.TYPE_INPUT_CONSUMER
Svetoslav8e3feb12014-02-24 13:46:47 -08001289 && windowType != WindowManager.LayoutParams.TYPE_POINTER
Svetoslav8e3feb12014-02-24 13:46:47 -08001290 && windowType != WindowManager.LayoutParams.TYPE_MAGNIFICATION_OVERLAY
1291 && windowType != WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA_OVERLAY
1292 && windowType != WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY
1293 && windowType != WindowManager.LayoutParams.TYPE_PRIVATE_PRESENTATION);
1294 }
1295
1296 private void populateVisibleWindowsOnScreenLocked(SparseArray<WindowState> outWindows) {
Wale Ogunwalef7cab102016-10-25 15:25:14 -07001297 final DisplayContent dc = mWindowManagerService.getDefaultDisplayContentLocked();
Wale Ogunwaled1880962016-11-08 10:31:59 -08001298 dc.forAllWindows((w) -> {
1299 if (w.isVisibleLw()) {
1300 outWindows.put(w.mLayer, w);
Svetoslav8e3feb12014-02-24 13:46:47 -08001301 }
Wale Ogunwaled1880962016-11-08 10:31:59 -08001302 }, false /* traverseTopToBottom */ );
Svetoslav8e3feb12014-02-24 13:46:47 -08001303 }
1304
1305 private class MyHandler extends Handler {
Svetoslavf7174e82014-06-12 11:29:35 -07001306 public static final int MESSAGE_COMPUTE_CHANGED_WINDOWS = 1;
Svetoslav8e3feb12014-02-24 13:46:47 -08001307
1308 public MyHandler(Looper looper) {
1309 super(looper, null, false);
1310 }
1311
1312 @Override
1313 @SuppressWarnings("unchecked")
1314 public void handleMessage(Message message) {
1315 switch (message.what) {
Svetoslavf7174e82014-06-12 11:29:35 -07001316 case MESSAGE_COMPUTE_CHANGED_WINDOWS: {
1317 computeChangedWindows();
1318 } break;
Svetoslav8e3feb12014-02-24 13:46:47 -08001319 }
1320 }
1321 }
1322 }
1323}