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