1/*
2 * Copyright (C) 2016 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 android.app.ActivityManager.TaskDescription;
20import android.app.ActivityManager.TaskSnapshot;
21import android.content.res.Configuration;
22import android.graphics.Rect;
23import android.os.Handler;
24import android.os.Looper;
25import android.os.Message;
26import android.util.EventLog;
27import android.util.Slog;
28import com.android.internal.annotations.VisibleForTesting;
29
30import java.lang.ref.WeakReference;
31
32import static com.android.server.EventLogTags.WM_TASK_CREATED;
33import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
34import static com.android.server.wm.WindowContainer.POSITION_BOTTOM;
35import static com.android.server.wm.WindowContainer.POSITION_TOP;
36import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STACK;
37import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
38
39/**
40 * Controller for the task container. This is created by activity manager to link task records to
41 * the task container they use in window manager.
42 *
43 * Test class: {@link TaskWindowContainerControllerTests}
44 */
45public class TaskWindowContainerController
46        extends WindowContainerController<Task, TaskWindowContainerListener> {
47
48    private final int mTaskId;
49    private final H mHandler;
50
51    public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
52            StackWindowController stackController, int userId, Rect bounds,
53            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
54            boolean homeTask, boolean toTop, boolean showForAllUsers,
55            TaskDescription taskDescription) {
56        this(taskId, listener, stackController, userId, bounds, overrideConfig, resizeMode,
57                supportsPictureInPicture, homeTask, toTop, showForAllUsers, taskDescription,
58                WindowManagerService.getInstance());
59    }
60
61    public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
62            StackWindowController stackController, int userId, Rect bounds,
63            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
64            boolean homeTask, boolean toTop, boolean showForAllUsers,
65            TaskDescription taskDescription, WindowManagerService service) {
66        super(listener, service);
67        mTaskId = taskId;
68        mHandler = new H(new WeakReference<>(this), service.mH.getLooper());
69
70        synchronized(mWindowMap) {
71            if (DEBUG_STACK) Slog.i(TAG_WM, "TaskWindowContainerController: taskId=" + taskId
72                    + " stack=" + stackController + " bounds=" + bounds);
73
74            final TaskStack stack = stackController.mContainer;
75            if (stack == null) {
76                throw new IllegalArgumentException("TaskWindowContainerController: invalid stack="
77                        + stackController);
78            }
79            EventLog.writeEvent(WM_TASK_CREATED, taskId, stack.mStackId);
80            final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
81                    supportsPictureInPicture, homeTask, taskDescription);
82            final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
83            stack.addTask(task, position, showForAllUsers, true /* moveParents */);
84        }
85    }
86
87    @VisibleForTesting
88    Task createTask(int taskId, TaskStack stack, int userId, Rect bounds,
89            Configuration overrideConfig, int resizeMode, boolean supportsPictureInPicture,
90            boolean homeTask, TaskDescription taskDescription) {
91        return new Task(taskId, stack, userId, mService, bounds, overrideConfig, resizeMode,
92                supportsPictureInPicture, homeTask, taskDescription, this);
93    }
94
95    @Override
96    public void removeContainer() {
97        synchronized(mWindowMap) {
98            if (mContainer == null) {
99                if (DEBUG_STACK) Slog.i(TAG_WM, "removeTask: could not find taskId=" + mTaskId);
100                return;
101            }
102            mContainer.removeIfPossible();
103            super.removeContainer();
104        }
105    }
106
107    public void positionChildAt(AppWindowContainerController childController, int position) {
108        synchronized(mService.mWindowMap) {
109            final AppWindowToken aToken = childController.mContainer;
110            if (aToken == null) {
111                Slog.w(TAG_WM,
112                        "Attempted to position of non-existing app : " + childController);
113                return;
114            }
115
116            final Task task = mContainer;
117            if (task == null) {
118                throw new IllegalArgumentException("positionChildAt: invalid task=" + this);
119            }
120            task.positionChildAt(position, aToken, false /* includeParents */);
121        }
122    }
123
124    public void reparent(StackWindowController stackController, int position, boolean moveParents) {
125        synchronized (mWindowMap) {
126            if (DEBUG_STACK) Slog.i(TAG_WM, "reparent: moving taskId=" + mTaskId
127                    + " to stack=" + stackController + " at " + position);
128            if (mContainer == null) {
129                if (DEBUG_STACK) Slog.i(TAG_WM,
130                        "reparent: could not find taskId=" + mTaskId);
131                return;
132            }
133            final TaskStack stack = stackController.mContainer;
134            if (stack == null) {
135                throw new IllegalArgumentException("reparent: could not find stack="
136                        + stackController);
137            }
138            mContainer.reparent(stack, position, moveParents);
139            mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
140        }
141    }
142
143    public void setResizeable(int resizeMode) {
144        synchronized (mWindowMap) {
145            if (mContainer != null) {
146                mContainer.setResizeable(resizeMode);
147            }
148        }
149    }
150
151    public void resize(Rect bounds, Configuration overrideConfig, boolean relayout,
152            boolean forced) {
153        synchronized (mWindowMap) {
154            if (mContainer == null) {
155                throw new IllegalArgumentException("resizeTask: taskId " + mTaskId + " not found.");
156            }
157
158            if (mContainer.resizeLocked(bounds, overrideConfig, forced) && relayout) {
159                mContainer.getDisplayContent().layoutAndAssignWindowLayersIfNeeded();
160            }
161        }
162    }
163
164    public void getBounds(Rect bounds) {
165        synchronized (mWindowMap) {
166            if (mContainer != null) {
167                mContainer.getBounds(bounds);
168                return;
169            }
170            bounds.setEmpty();
171        }
172    }
173
174    /**
175     * Puts this task into docked drag resizing mode. See {@link DragResizeMode}.
176     *
177     * @param resizing Whether to put the task into drag resize mode.
178     */
179    public void setTaskDockedResizing(boolean resizing) {
180        synchronized (mWindowMap) {
181            if (mContainer == null) {
182                Slog.w(TAG_WM, "setTaskDockedResizing: taskId " + mTaskId + " not found.");
183                return;
184            }
185            mContainer.setDragResizing(resizing, DRAG_RESIZE_MODE_DOCKED_DIVIDER);
186        }
187    }
188
189    public void cancelWindowTransition() {
190        synchronized (mWindowMap) {
191            if (mContainer == null) {
192                Slog.w(TAG_WM, "cancelWindowTransition: taskId " + mTaskId + " not found.");
193                return;
194            }
195            mContainer.cancelTaskWindowTransition();
196        }
197    }
198
199    public void cancelThumbnailTransition() {
200        synchronized (mWindowMap) {
201            if (mContainer == null) {
202                Slog.w(TAG_WM, "cancelThumbnailTransition: taskId " + mTaskId + " not found.");
203                return;
204            }
205            mContainer.cancelTaskThumbnailTransition();
206        }
207    }
208
209    public void setTaskDescription(TaskDescription taskDescription) {
210        synchronized (mWindowMap) {
211            if (mContainer == null) {
212                Slog.w(TAG_WM, "setTaskDescription: taskId " + mTaskId + " not found.");
213                return;
214            }
215            mContainer.setTaskDescription(taskDescription);
216        }
217    }
218
219    void reportSnapshotChanged(TaskSnapshot snapshot) {
220        mHandler.obtainMessage(H.REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
221    }
222
223    void requestResize(Rect bounds, int resizeMode) {
224        mHandler.obtainMessage(H.REQUEST_RESIZE, resizeMode, 0, bounds).sendToTarget();
225    }
226
227    @Override
228    public String toString() {
229        return "{TaskWindowContainerController taskId=" + mTaskId + "}";
230    }
231
232    private static final class H extends Handler {
233
234        static final int REPORT_SNAPSHOT_CHANGED = 0;
235        static final int REQUEST_RESIZE = 1;
236
237        private final WeakReference<TaskWindowContainerController> mController;
238
239        H(WeakReference<TaskWindowContainerController> controller, Looper looper) {
240            super(looper);
241            mController = controller;
242        }
243
244        @Override
245        public void handleMessage(Message msg) {
246            final TaskWindowContainerController controller = mController.get();
247            final TaskWindowContainerListener listener = (controller != null)
248                    ? controller.mListener : null;
249            if (listener == null) {
250                return;
251            }
252            switch (msg.what) {
253                case REPORT_SNAPSHOT_CHANGED:
254                    listener.onSnapshotChanged((TaskSnapshot) msg.obj);
255                    break;
256                case REQUEST_RESIZE:
257                    listener.requestResize((Rect) msg.obj, msg.arg1);
258                    break;
259            }
260        }
261    }
262}
263