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.IActivityManager;
24 import android.os.RemoteException;
25 import android.os.Handler;
26 import android.os.Looper;
27 import android.util.Slog;
28 import android.view.Display;
29 import android.view.IWindow;
30 import com.android.internal.annotations.GuardedBy;
31 import com.android.server.input.InputManagerService;
32 import com.android.server.input.InputWindowHandle;
33 
34 /**
35  * Controller for task positioning by drag.
36  */
37 class TaskPositioningController {
38     private final WindowManagerService mService;
39     private final InputManagerService mInputManager;
40     private final InputMonitor mInputMonitor;
41     private final IActivityManager mActivityManager;
42     private final Handler mHandler;
43 
44     @GuardedBy("WindowManagerSerivce.mWindowMap")
45     private @Nullable TaskPositioner mTaskPositioner;
46 
isPositioningLocked()47     boolean isPositioningLocked() {
48         return mTaskPositioner != null;
49     }
50 
getDragWindowHandleLocked()51     InputWindowHandle getDragWindowHandleLocked() {
52         return mTaskPositioner != null ? mTaskPositioner.mDragWindowHandle : null;
53     }
54 
TaskPositioningController(WindowManagerService service, InputManagerService inputManager, InputMonitor inputMonitor, IActivityManager activityManager, Looper looper)55     TaskPositioningController(WindowManagerService service, InputManagerService inputManager,
56             InputMonitor inputMonitor, IActivityManager activityManager, Looper looper) {
57         mService = service;
58         mInputMonitor = inputMonitor;
59         mInputManager = inputManager;
60         mActivityManager = activityManager;
61         mHandler = new Handler(looper);
62     }
63 
startMovingTask(IWindow window, float startX, float startY)64     boolean startMovingTask(IWindow window, float startX, float startY) {
65         WindowState win = null;
66         synchronized (mService.mWindowMap) {
67             win = mService.windowForClientLocked(null, window, false);
68             // win shouldn't be null here, pass it down to startPositioningLocked
69             // to get warning if it's null.
70             if (!startPositioningLocked(
71                     win, false /*resize*/, false /*preserveOrientation*/, startX, startY)) {
72                 return false;
73             }
74         }
75         try {
76             mActivityManager.setFocusedTask(win.getTask().mTaskId);
77         } catch(RemoteException e) {}
78         return true;
79     }
80 
handleTapOutsideTask(DisplayContent displayContent, int x, int y)81     void handleTapOutsideTask(DisplayContent displayContent, int x, int y) {
82         mHandler.post(() -> {
83             int taskId = -1;
84             synchronized (mService.mWindowMap) {
85                 final Task task = displayContent.findTaskForResizePoint(x, y);
86                 if (task != null) {
87                     if (!startPositioningLocked(task.getTopVisibleAppMainWindow(), true /*resize*/,
88                             task.preserveOrientationOnResize(), x, y)) {
89                         return;
90                     }
91                     taskId = task.mTaskId;
92                 } else {
93                     taskId = displayContent.taskIdFromPoint(x, y);
94                 }
95             }
96             if (taskId >= 0) {
97                 try {
98                     mActivityManager.setFocusedTask(taskId);
99                 } catch (RemoteException e) {
100                 }
101             }
102         });
103     }
104 
startPositioningLocked(WindowState win, boolean resize, boolean preserveOrientation, float startX, float startY)105     private boolean startPositioningLocked(WindowState win, boolean resize,
106             boolean preserveOrientation, float startX, float startY) {
107         if (DEBUG_TASK_POSITIONING)
108             Slog.d(TAG_WM, "startPositioningLocked: "
109                     + "win=" + win + ", resize=" + resize + ", preserveOrientation="
110                     + preserveOrientation + ", {" + startX + ", " + startY + "}");
111 
112         if (win == null || win.getAppToken() == null) {
113             Slog.w(TAG_WM, "startPositioningLocked: Bad window " + win);
114             return false;
115         }
116         if (win.mInputChannel == null) {
117             Slog.wtf(TAG_WM, "startPositioningLocked: " + win + " has no input channel, "
118                     + " probably being removed");
119             return false;
120         }
121 
122         final DisplayContent displayContent = win.getDisplayContent();
123         if (displayContent == null) {
124             Slog.w(TAG_WM, "startPositioningLocked: Invalid display content " + win);
125             return false;
126         }
127 
128         Display display = displayContent.getDisplay();
129         mTaskPositioner = TaskPositioner.create(mService);
130         mTaskPositioner.register(displayContent);
131         mInputMonitor.updateInputWindowsLw(true /*force*/);
132 
133         // We need to grab the touch focus so that the touch events during the
134         // resizing/scrolling are not sent to the app. 'win' is the main window
135         // of the app, it may not have focus since there might be other windows
136         // on top (eg. a dialog window).
137         WindowState transferFocusFromWin = win;
138         if (mService.mCurrentFocus != null && mService.mCurrentFocus != win
139                 && mService.mCurrentFocus.mAppToken == win.mAppToken) {
140             transferFocusFromWin = mService.mCurrentFocus;
141         }
142         if (!mInputManager.transferTouchFocus(
143                 transferFocusFromWin.mInputChannel, mTaskPositioner.mServerChannel)) {
144             Slog.e(TAG_WM, "startPositioningLocked: Unable to transfer touch focus");
145             mTaskPositioner.unregister();
146             mTaskPositioner = null;
147             mInputMonitor.updateInputWindowsLw(true /*force*/);
148             return false;
149         }
150 
151         mTaskPositioner.startDrag(win, resize, preserveOrientation, startX, startY);
152         return true;
153     }
154 
finishTaskPositioning()155     void finishTaskPositioning() {
156         mHandler.post(() -> {
157             if (DEBUG_TASK_POSITIONING) Slog.d(TAG_WM, "finishPositioning");
158 
159             synchronized (mService.mWindowMap) {
160                 if (mTaskPositioner != null) {
161                     mTaskPositioner.unregister();
162                     mTaskPositioner = null;
163                     mInputMonitor.updateInputWindowsLw(true /*force*/);
164                 }
165             }
166         });
167     }
168 }
169