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