blob: 44a6fc936961d082ab74a44567ad27ee74978548 [file] [log] [blame]
Robert Carr8a2f9132019-11-11 15:03:15 -08001/*
2 * Copyright (C) 2019 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
Evan Rosky0037e5f2019-11-05 10:26:24 -080019import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
Robert Carr00c0dbe2020-01-24 15:30:24 -080020import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW;
Evan Rosky0037e5f2019-11-05 10:26:24 -080021import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
22import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_PRIMARY;
23import static android.app.WindowConfiguration.WINDOWING_MODE_SPLIT_SCREEN_SECONDARY;
Robert Carr8a2f9132019-11-11 15:03:15 -080024
Evan Rosky0037e5f2019-11-05 10:26:24 -080025import static com.android.server.wm.ActivityStackSupervisor.PRESERVE_WINDOWS;
26
27import android.annotation.Nullable;
28import android.app.ActivityManager.RunningTaskInfo;
29import android.app.ITaskOrganizerController;
30import android.app.WindowConfiguration;
31import android.content.pm.ActivityInfo;
32import android.content.res.Configuration;
33import android.graphics.Rect;
34import android.os.Binder;
Robert Carr8a2f9132019-11-11 15:03:15 -080035import android.os.IBinder;
36import android.os.RemoteException;
Evan Rosky0037e5f2019-11-05 10:26:24 -080037import android.util.ArraySet;
Robert Carr8a2f9132019-11-11 15:03:15 -080038import android.util.Slog;
39import android.view.ITaskOrganizer;
Evan Rosky0037e5f2019-11-05 10:26:24 -080040import android.view.IWindowContainer;
41import android.view.WindowContainerTransaction;
42
43import com.android.internal.util.function.pooled.PooledConsumer;
44import com.android.internal.util.function.pooled.PooledLambda;
Robert Carr8a2f9132019-11-11 15:03:15 -080045
46import java.util.ArrayList;
47import java.util.HashMap;
Evan Rosky0037e5f2019-11-05 10:26:24 -080048import java.util.Iterator;
49import java.util.Map;
50import java.util.WeakHashMap;
Robert Carr8a2f9132019-11-11 15:03:15 -080051
52/**
53 * Stores the TaskOrganizers associated with a given windowing mode and
54 * their associated state.
55 */
Evan Rosky0037e5f2019-11-05 10:26:24 -080056class TaskOrganizerController extends ITaskOrganizerController.Stub {
Robert Carr8a2f9132019-11-11 15:03:15 -080057 private static final String TAG = "TaskOrganizerController";
58
Evan Rosky0037e5f2019-11-05 10:26:24 -080059 /** Flag indicating that an applied transaction may have effected lifecycle */
60 private static final int TRANSACT_EFFECTS_CLIENT_CONFIG = 1;
61 private static final int TRANSACT_EFFECTS_LIFECYCLE = 1 << 1;
62
63 private final WindowManagerGlobalLock mGlobalLock;
Robert Carr8a2f9132019-11-11 15:03:15 -080064
65 private class DeathRecipient implements IBinder.DeathRecipient {
66 int mWindowingMode;
67 ITaskOrganizer mTaskOrganizer;
68
69 DeathRecipient(ITaskOrganizer organizer, int windowingMode) {
70 mTaskOrganizer = organizer;
71 mWindowingMode = windowingMode;
72 }
73
74 @Override
75 public void binderDied() {
76 synchronized (mGlobalLock) {
77 final TaskOrganizerState state = mTaskOrganizerStates.get(mTaskOrganizer);
78 for (int i = 0; i < state.mOrganizedTasks.size(); i++) {
79 state.mOrganizedTasks.get(i).taskOrganizerDied();
80 }
81 mTaskOrganizerStates.remove(mTaskOrganizer);
82 if (mTaskOrganizersForWindowingMode.get(mWindowingMode) == mTaskOrganizer) {
83 mTaskOrganizersForWindowingMode.remove(mWindowingMode);
84 }
85 }
86 }
87 };
88
89 class TaskOrganizerState {
90 ITaskOrganizer mOrganizer;
91 DeathRecipient mDeathRecipient;
92
93 ArrayList<Task> mOrganizedTasks = new ArrayList<>();
94
95 void addTask(Task t) {
96 mOrganizedTasks.add(t);
97 }
98
99 void removeTask(Task t) {
100 mOrganizedTasks.remove(t);
101 }
102
103 TaskOrganizerState(ITaskOrganizer organizer, DeathRecipient deathRecipient) {
104 mOrganizer = organizer;
105 mDeathRecipient = deathRecipient;
106 }
107 };
108
109
110 final HashMap<Integer, TaskOrganizerState> mTaskOrganizersForWindowingMode = new HashMap();
111 final HashMap<ITaskOrganizer, TaskOrganizerState> mTaskOrganizerStates = new HashMap();
112
113 final HashMap<Integer, ITaskOrganizer> mTaskOrganizersByPendingSyncId = new HashMap();
114
Evan Rosky0037e5f2019-11-05 10:26:24 -0800115 private final WeakHashMap<Task, RunningTaskInfo> mLastSentTaskInfos = new WeakHashMap<>();
116 private final ArrayList<Task> mPendingTaskInfoChanges = new ArrayList<>();
117
Robert Carr8a2f9132019-11-11 15:03:15 -0800118 final ActivityTaskManagerService mService;
119
Evan Rosky0037e5f2019-11-05 10:26:24 -0800120 RunningTaskInfo mTmpTaskInfo;
121
122 TaskOrganizerController(ActivityTaskManagerService atm) {
Robert Carr8a2f9132019-11-11 15:03:15 -0800123 mService = atm;
Evan Rosky0037e5f2019-11-05 10:26:24 -0800124 mGlobalLock = atm.mGlobalLock;
125 }
126
127 private void enforceStackPermission(String func) {
128 mService.mAmInternal.enforceCallingPermission(MANAGE_ACTIVITY_STACKS, func);
Robert Carr8a2f9132019-11-11 15:03:15 -0800129 }
130
131 private void clearIfNeeded(int windowingMode) {
132 final TaskOrganizerState oldState = mTaskOrganizersForWindowingMode.get(windowingMode);
133 if (oldState != null) {
134 oldState.mOrganizer.asBinder().unlinkToDeath(oldState.mDeathRecipient, 0);
135 }
136 }
137
138 /**
139 * Register a TaskOrganizer to manage tasks as they enter the given windowing mode.
140 * If there was already a TaskOrganizer for this windowing mode it will be evicted
141 * and receive taskVanished callbacks in the process.
142 */
Evan Rosky0037e5f2019-11-05 10:26:24 -0800143 @Override
144 public void registerTaskOrganizer(ITaskOrganizer organizer, int windowingMode) {
145 if (windowingMode != WINDOWING_MODE_PINNED
146 && windowingMode != WINDOWING_MODE_SPLIT_SCREEN_PRIMARY
147 && windowingMode != WINDOWING_MODE_SPLIT_SCREEN_SECONDARY
148 && windowingMode != WINDOWING_MODE_MULTI_WINDOW) {
149 throw new UnsupportedOperationException("As of now only Pinned/Split/Multiwindow"
150 + " windowing modes are supported for registerTaskOrganizer");
Robert Carr8a2f9132019-11-11 15:03:15 -0800151 }
Evan Rosky0037e5f2019-11-05 10:26:24 -0800152 enforceStackPermission("registerTaskOrganizer()");
153 final long origId = Binder.clearCallingIdentity();
Robert Carr8a2f9132019-11-11 15:03:15 -0800154 try {
Evan Rosky0037e5f2019-11-05 10:26:24 -0800155 synchronized (mGlobalLock) {
156 clearIfNeeded(windowingMode);
157 DeathRecipient dr = new DeathRecipient(organizer, windowingMode);
158 try {
159 organizer.asBinder().linkToDeath(dr, 0);
160 } catch (RemoteException e) {
161 Slog.e(TAG, "TaskOrganizer failed to register death recipient");
162 }
163
164 final TaskOrganizerState state = new TaskOrganizerState(organizer, dr);
165 mTaskOrganizersForWindowingMode.put(windowingMode, state);
166
167 mTaskOrganizerStates.put(organizer, state);
168 }
169 } finally {
170 Binder.restoreCallingIdentity(origId);
Robert Carr8a2f9132019-11-11 15:03:15 -0800171 }
Robert Carr8a2f9132019-11-11 15:03:15 -0800172 }
173
174 ITaskOrganizer getTaskOrganizer(int windowingMode) {
175 final TaskOrganizerState state = mTaskOrganizersForWindowingMode.get(windowingMode);
176 if (state == null) {
177 return null;
178 }
179 return state.mOrganizer;
180 }
181
182 private void sendTaskAppeared(ITaskOrganizer organizer, Task task) {
183 try {
Evan Rosky0037e5f2019-11-05 10:26:24 -0800184 organizer.taskAppeared(task.getTaskInfo());
Robert Carr8a2f9132019-11-11 15:03:15 -0800185 } catch (Exception e) {
186 Slog.e(TAG, "Exception sending taskAppeared callback" + e);
187 }
188 }
189
190 private void sendTaskVanished(ITaskOrganizer organizer, Task task) {
191 try {
192 organizer.taskVanished(task.getRemoteToken());
193 } catch (Exception e) {
194 Slog.e(TAG, "Exception sending taskVanished callback" + e);
195 }
196 }
197
198 void onTaskAppeared(ITaskOrganizer organizer, Task task) {
199 TaskOrganizerState state = mTaskOrganizerStates.get(organizer);
200
201 state.addTask(task);
202 sendTaskAppeared(organizer, task);
203 }
204
205 void onTaskVanished(ITaskOrganizer organizer, Task task) {
206 final TaskOrganizerState state = mTaskOrganizerStates.get(organizer);
207 sendTaskVanished(organizer, task);
208
209 // This could trigger TaskAppeared for other tasks in the same stack so make sure
210 // we do this AFTER sending taskVanished.
211 state.removeTask(task);
212 }
Evan Rosky0037e5f2019-11-05 10:26:24 -0800213
214 @Override
215 public RunningTaskInfo createRootTask(int displayId, int windowingMode) {
216 enforceStackPermission("createRootTask()");
217 final long origId = Binder.clearCallingIdentity();
218 try {
219 synchronized (mGlobalLock) {
220 DisplayContent display = mService.mRootWindowContainer.getDisplayContent(displayId);
221 if (display == null) {
222 return null;
223 }
224 final int nextId = display.getNextStackId();
225 TaskTile tile = new TaskTile(mService, nextId, windowingMode);
226 display.addTile(tile);
227 RunningTaskInfo out = new RunningTaskInfo();
228 tile.fillTaskInfo(out);
229 mLastSentTaskInfos.put(tile, out);
230 return out;
231 }
232 } finally {
233 Binder.restoreCallingIdentity(origId);
234 }
235 }
236
237 @Override
238 public boolean deleteRootTask(IWindowContainer token) {
239 enforceStackPermission("deleteRootTask()");
240 final long origId = Binder.clearCallingIdentity();
241 try {
242 synchronized (mGlobalLock) {
243 TaskTile tile = TaskTile.forToken(token.asBinder());
244 if (tile == null) {
245 return false;
246 }
247 tile.removeImmediately();
248 return true;
249 }
250 } finally {
251 Binder.restoreCallingIdentity(origId);
252 }
253 }
254
255 void dispatchPendingTaskInfoChanges() {
256 if (mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
257 return;
258 }
259 for (int i = 0, n = mPendingTaskInfoChanges.size(); i < n; ++i) {
260 dispatchTaskInfoChanged(mPendingTaskInfoChanges.get(i), false /* force */);
261 }
262 mPendingTaskInfoChanges.clear();
263 }
264
265 void dispatchTaskInfoChanged(Task task, boolean force) {
266 if (!force && mService.mWindowManager.mWindowPlacerLocked.isLayoutDeferred()) {
267 // Defer task info reporting while layout is deferred. This is because layout defer
268 // blocks tend to do lots of re-ordering which can mess up animations in receivers.
269 mPendingTaskInfoChanges.remove(task);
270 mPendingTaskInfoChanges.add(task);
271 return;
272 }
273 RunningTaskInfo lastInfo = mLastSentTaskInfos.get(task);
274 if (mTmpTaskInfo == null) {
275 mTmpTaskInfo = new RunningTaskInfo();
276 }
277 task.fillTaskInfo(mTmpTaskInfo);
278 boolean changed = lastInfo == null
279 || mTmpTaskInfo.topActivityType != lastInfo.topActivityType
280 || mTmpTaskInfo.isResizable() != lastInfo.isResizable();
281 if (!(changed || force)) {
282 return;
283 }
284 final RunningTaskInfo newInfo = mTmpTaskInfo;
285 mLastSentTaskInfos.put(task, mTmpTaskInfo);
286 // Since we've stored this, clean up the reference so a new one will be created next time.
287 // Transferring it this way means we only have to construct new RunningTaskInfos when they
288 // change.
289 mTmpTaskInfo = null;
290
291 if (task.mTaskOrganizer != null) {
292 try {
293 task.mTaskOrganizer.onTaskInfoChanged(newInfo);
294 } catch (RemoteException e) {
295 }
296 }
297 }
298
299 @Override
300 public IWindowContainer getImeTarget(int displayId) {
301 enforceStackPermission("getImeTarget()");
302 final long origId = Binder.clearCallingIdentity();
303 try {
304 synchronized (mGlobalLock) {
305 DisplayContent dc = mService.mWindowManager.mRoot
306 .getDisplayContent(displayId);
307 if (dc == null || dc.mInputMethodTarget == null) {
308 return null;
309 }
310 // Avoid WindowState#getRootTask() so we don't attribute system windows to a task.
311 final Task task = dc.mInputMethodTarget.getTask();
312 if (task == null) {
313 return null;
314 }
315 ActivityStack rootTask = (ActivityStack) task.getRootTask();
316 final TaskTile tile = rootTask.getTile();
317 if (tile != null) {
318 rootTask = tile;
319 }
320 return rootTask.mRemoteToken;
321 }
322 } finally {
323 Binder.restoreCallingIdentity(origId);
324 }
325 }
326
327 @Override
328 public void setLaunchRoot(int displayId, @Nullable IWindowContainer tile) {
329 enforceStackPermission("setLaunchRoot()");
330 final long origId = Binder.clearCallingIdentity();
331 try {
332 synchronized (mGlobalLock) {
333 DisplayContent display = mService.mRootWindowContainer.getDisplayContent(displayId);
334 if (display == null) {
335 return;
336 }
337 TaskTile taskTile = tile == null ? null : TaskTile.forToken(tile.asBinder());
338 if (taskTile == null) {
339 display.mLaunchTile = null;
340 return;
341 }
342 if (taskTile.getDisplay() != display) {
343 throw new RuntimeException("Can't set launch root for display " + displayId
344 + " to task on display " + taskTile.getDisplay().getDisplayId());
345 }
346 display.mLaunchTile = taskTile;
347 }
348 } finally {
349 Binder.restoreCallingIdentity(origId);
350 }
351 }
352
353 private int sanitizeAndApplyChange(WindowContainer container,
354 WindowContainerTransaction.Change change) {
355 if (!(container instanceof Task)) {
356 throw new RuntimeException("Invalid token in task transaction");
357 }
358 // The "client"-facing API should prevent bad changes; however, just in case, sanitize
359 // masks here.
360 int configMask = change.getConfigSetMask();
361 int windowMask = change.getWindowSetMask();
362 configMask &= ActivityInfo.CONFIG_WINDOW_CONFIGURATION
363 | ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
364 windowMask &= WindowConfiguration.WINDOW_CONFIG_BOUNDS;
365 int effects = 0;
366 if (configMask != 0) {
367 Configuration c = new Configuration(container.getRequestedOverrideConfiguration());
368 c.setTo(change.getConfiguration(), configMask, windowMask);
369 container.onRequestedOverrideConfigurationChanged(c);
370 // TODO(b/145675353): remove the following once we could apply new bounds to the
371 // pinned stack together with its children.
372 resizePinnedStackIfNeeded(container, configMask, windowMask, c);
373 effects |= TRANSACT_EFFECTS_CLIENT_CONFIG;
374 }
375 if ((change.getChangeMask() & WindowContainerTransaction.Change.CHANGE_FOCUSABLE) != 0) {
376 if (container.setFocusable(change.getFocusable())) {
377 effects |= TRANSACT_EFFECTS_LIFECYCLE;
378 }
379 }
380 return effects;
381 }
382
383 private void resizePinnedStackIfNeeded(ConfigurationContainer container, int configMask,
384 int windowMask, Configuration config) {
385 if ((container instanceof ActivityStack)
386 && ((configMask & ActivityInfo.CONFIG_WINDOW_CONFIGURATION) != 0)
387 && ((windowMask & WindowConfiguration.WINDOW_CONFIG_BOUNDS) != 0)) {
388 final ActivityStack stack = (ActivityStack) container;
389 if (stack.inPinnedWindowingMode()) {
390 stack.resize(config.windowConfiguration.getBounds(),
391 null /* tempTaskBounds */, null /* tempTaskInsetBounds */,
392 PRESERVE_WINDOWS, true /* deferResume */);
393 }
394 }
395 }
396
397 private int applyWindowContainerChange(WindowContainer wc,
398 WindowContainerTransaction.Change c) {
399 int effects = sanitizeAndApplyChange(wc, c);
400
401 Rect enterPipBounds = c.getEnterPipBounds();
402 if (enterPipBounds != null) {
403 Task tr = (Task) wc;
404 mService.mStackSupervisor.updatePictureInPictureMode(tr,
405 enterPipBounds, true);
406 }
407 return effects;
408 }
409
410 @Override
411 public void applyContainerTransaction(WindowContainerTransaction t) {
412 enforceStackPermission("applyContainerTransaction()");
413 if (t == null) {
414 return;
415 }
416 long ident = Binder.clearCallingIdentity();
417 try {
418 synchronized (mGlobalLock) {
419 int effects = 0;
420 mService.deferWindowLayout();
421 try {
422 ArraySet<WindowContainer> haveConfigChanges = new ArraySet<>();
423 Iterator<Map.Entry<IBinder, WindowContainerTransaction.Change>> entries =
424 t.getChanges().entrySet().iterator();
425 while (entries.hasNext()) {
426 final Map.Entry<IBinder, WindowContainerTransaction.Change> entry =
427 entries.next();
428 final WindowContainer wc = WindowContainer.RemoteToken.fromBinder(
429 entry.getKey()).getContainer();
430 int containerEffect = applyWindowContainerChange(wc, entry.getValue());
431 effects |= containerEffect;
432 // Lifecycle changes will trigger ensureConfig for everything.
433 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) == 0
434 && (containerEffect & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
435 haveConfigChanges.add(wc);
436 }
437 }
438 if ((effects & TRANSACT_EFFECTS_LIFECYCLE) != 0) {
439 // Already calls ensureActivityConfig
440 mService.mRootWindowContainer.ensureActivitiesVisible(
441 null, 0, PRESERVE_WINDOWS);
442 } else if ((effects & TRANSACT_EFFECTS_CLIENT_CONFIG) != 0) {
443 final PooledConsumer f = PooledLambda.obtainConsumer(
444 ActivityRecord::ensureActivityConfiguration,
445 PooledLambda.__(ActivityRecord.class), 0,
446 false /* preserveWindow */);
447 try {
448 for (int i = haveConfigChanges.size() - 1; i >= 0; --i) {
449 haveConfigChanges.valueAt(i).forAllActivities(f);
450 }
451 } finally {
452 f.recycle();
453 }
454 }
455 } finally {
456 mService.continueWindowLayout();
457 }
458 }
459 } finally {
460 Binder.restoreCallingIdentity(ident);
461 }
462 }
Robert Carr8a2f9132019-11-11 15:03:15 -0800463}