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
17package com.android.server.wm;
18
19import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_TASK_POSITIONING;
20import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
21
22import android.annotation.Nullable;
23import android.app.IActivityManager;
24import android.os.RemoteException;
25import android.os.Handler;
26import android.os.Looper;
27import android.util.Slog;
28import android.view.Display;
29import android.view.IWindow;
30import com.android.internal.annotations.GuardedBy;
31import com.android.server.input.InputManagerService;
32import com.android.server.input.InputWindowHandle;
33
34/**
35 * Controller for task positioning by drag.
36 */
37class 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
47    boolean isPositioningLocked() {
48        return mTaskPositioner != null;
49    }
50
51    InputWindowHandle getDragWindowHandleLocked() {
52        return mTaskPositioner != null ? mTaskPositioner.mDragWindowHandle : null;
53    }
54
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
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
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
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
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