1 /* 2 * Copyright (C) 2017 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 com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING; 20 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM; 21 22 import android.annotation.Nullable; 23 import android.graphics.Point; 24 import android.graphics.Rect; 25 import android.util.Slog; 26 import android.view.Display; 27 import android.view.IWindow; 28 import android.view.InputWindowHandle; 29 import android.view.SurfaceControl; 30 31 /** 32 * Controller for task positioning by drag. 33 */ 34 class TaskPositioningController { 35 private final WindowManagerService mService; 36 private SurfaceControl mInputSurface; 37 private DisplayContent mPositioningDisplay; 38 39 private @Nullable TaskPositioner mTaskPositioner; 40 41 private final Rect mTmpClipRect = new Rect(); 42 isPositioningLocked()43 boolean isPositioningLocked() { 44 return mTaskPositioner != null; 45 } 46 47 final SurfaceControl.Transaction mTransaction; 48 getDragWindowHandleLocked()49 InputWindowHandle getDragWindowHandleLocked() { 50 return mTaskPositioner != null ? mTaskPositioner.mDragWindowHandle : null; 51 } 52 TaskPositioningController(WindowManagerService service)53 TaskPositioningController(WindowManagerService service) { 54 mService = service; 55 mTransaction = service.mTransactionFactory.get(); 56 } 57 hideInputSurface(int displayId)58 void hideInputSurface(int displayId) { 59 if (mPositioningDisplay != null && mPositioningDisplay.getDisplayId() == displayId 60 && mInputSurface != null) { 61 mTransaction.hide(mInputSurface); 62 mTransaction.syncInputWindows().apply(); 63 } 64 } 65 showInputSurface(int displayId)66 void showInputSurface(int displayId) { 67 if (mPositioningDisplay == null || mPositioningDisplay.getDisplayId() != displayId) { 68 return; 69 } 70 final DisplayContent dc = mService.mRoot.getDisplayContent(displayId); 71 if (mInputSurface == null) { 72 mInputSurface = mService.makeSurfaceBuilder(dc.getSession()) 73 .setContainerLayer() 74 .setName("Drag and Drop Input Consumer") 75 .setCallsite("TaskPositioningController.showInputSurface") 76 .setParent(dc.getOverlayLayer()) 77 .build(); 78 } 79 80 final InputWindowHandle h = getDragWindowHandleLocked(); 81 if (h == null) { 82 Slog.w(TAG_WM, "Drag is in progress but there is no " 83 + "drag window handle."); 84 return; 85 } 86 87 final Display display = dc.getDisplay(); 88 final Point p = new Point(); 89 display.getRealSize(p); 90 mTmpClipRect.set(0, 0, p.x, p.y); 91 92 mTransaction.show(mInputSurface) 93 .setInputWindowInfo(mInputSurface, h) 94 .setLayer(mInputSurface, Integer.MAX_VALUE) 95 .setPosition(mInputSurface, 0, 0) 96 .setCrop(mInputSurface, mTmpClipRect) 97 .syncInputWindows() 98 .apply(); 99 } 100 startMovingTask(IWindow window, float startX, float startY)101 boolean startMovingTask(IWindow window, float startX, float startY) { 102 WindowState win = null; 103 synchronized (mService.mGlobalLock) { 104 win = mService.windowForClientLocked(null, window, false); 105 // win shouldn't be null here, pass it down to startPositioningLocked 106 // to get warning if it's null. 107 if (!startPositioningLocked( 108 win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) { 109 return false; 110 } 111 mService.mAtmService.setFocusedTask(win.getTask().mTaskId); 112 } 113 return true; 114 } 115 handleTapOutsideTask(DisplayContent displayContent, int x, int y)116 void handleTapOutsideTask(DisplayContent displayContent, int x, int y) { 117 mService.mH.post(() -> { 118 synchronized (mService.mGlobalLock) { 119 final Task task = displayContent.findTaskForResizePoint(x, y); 120 if (task != null) { 121 if (!task.isResizeable()) { 122 // The task is not resizable, so don't do anything when the user drags the 123 // the resize handles. 124 return; 125 } 126 if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/, 127 task.preserveOrientationOnResize(), x, y)) { 128 return; 129 } 130 mService.mAtmService.setFocusedTask(task.mTaskId); 131 } 132 } 133 }); 134 } 135 startPositioningLocked(WindowState win, boolean resize, boolean preserveOrientation, float startX, float startY)136 private boolean startPositioningLocked(WindowState win, boolean resize, 137 boolean preserveOrientation, float startX, float startY) { 138 if (DEBUG_TASK_POSITIONING) 139 Slog.d(TAG_WM, "startPositioningLocked: " 140 + "win=" + win + ", resize=" + resize + ", preserveOrientation=" 141 + preserveOrientation + ", {" + startX + ", " + startY + "}"); 142 143 if (win == null || win.mActivityRecord == null) { 144 Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win); 145 return false; 146 } 147 if (win.mInputChannel == null) { 148 Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, " 149 + " probably being removed"); 150 return false; 151 } 152 153 final DisplayContent displayContent = win.getDisplayContent(); 154 if (displayContent == null) { 155 Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win); 156 return false; 157 } 158 mPositioningDisplay = displayContent; 159 160 mTaskPositioner = TaskPositioner.create(mService); 161 mTaskPositioner.register(displayContent, win); 162 163 // We need to grab the touch focus so that the touch events during the 164 // resizing/scrolling are not sent to the app. 'win' is the main window 165 // of the app, it may not have focus since there might be other windows 166 // on top (eg. a dialog window). 167 WindowState transferFocusFromWin = win; 168 if (displayContent.mCurrentFocus != null && displayContent.mCurrentFocus != win 169 && displayContent.mCurrentFocus.mActivityRecord == win.mActivityRecord) { 170 transferFocusFromWin = displayContent.mCurrentFocus; 171 } 172 if (!mService.mInputManager.transferTouchFocus( 173 transferFocusFromWin.mInputChannel, mTaskPositioner.mClientChannel, 174 false /* isDragDrop */)) { 175 Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus"); 176 cleanUpTaskPositioner(); 177 return false; 178 } 179 180 mTaskPositioner.startDrag(resize, preserveOrientation, startX, startY); 181 return true; 182 } 183 finishTaskPositioning(IWindow window)184 public void finishTaskPositioning(IWindow window) { 185 if (mTaskPositioner != null && mTaskPositioner.mClientCallback == window.asBinder()) { 186 finishTaskPositioning(); 187 } 188 } 189 finishTaskPositioning()190 void finishTaskPositioning() { 191 // TaskPositioner attaches the InputEventReceiver to the animation thread. We need to 192 // dispose the receiver on the same thread to avoid race conditions. 193 mService.mAnimationHandler.post(() -> { 194 if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning"); 195 196 synchronized (mService.mGlobalLock) { 197 cleanUpTaskPositioner(); 198 mPositioningDisplay = null; 199 } 200 }); 201 } 202 cleanUpTaskPositioner()203 private void cleanUpTaskPositioner() { 204 final TaskPositioner positioner = mTaskPositioner; 205 if (positioner == null) { 206 return; 207 } 208 209 // We need to assign task positioner to null first to indicate that we're finishing task 210 // positioning. 211 mTaskPositioner = null; 212 positioner.unregister(); 213 } 214 } 215