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