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