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