blob: 2b74f84701b49822615c153fe45db206a6f87cf1 [file] [log] [blame]
Jorim Jaggi02886a82016-12-06 09:10:06 -08001/*
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 static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
Jorim Jaggi02886a82016-12-06 09:10:06 -080020
21import android.annotation.Nullable;
22import android.app.ActivityManager.StackId;
Jorim Jaggie2c77f92016-12-29 14:57:22 +010023import android.app.ActivityManager.TaskSnapshot;
Jorim Jaggi02886a82016-12-06 09:10:06 -080024import android.graphics.GraphicBuffer;
Jorim Jaggif9084ec2017-01-16 13:16:59 +010025import android.os.Environment;
Jorim Jaggi02886a82016-12-06 09:10:06 -080026import android.util.ArraySet;
27import android.view.WindowManagerPolicy.StartingSurface;
28
Jorim Jaggi8b702ed2017-01-20 16:59:03 +010029import com.google.android.collect.Sets;
30
Jorim Jaggi02886a82016-12-06 09:10:06 -080031import com.android.internal.annotations.VisibleForTesting;
32
Jorim Jaggi10abe2f2017-01-03 16:44:46 +010033import java.io.PrintWriter;
34
Jorim Jaggi02886a82016-12-06 09:10:06 -080035/**
36 * When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
37 * put it into our cache. Internally we use gralloc buffers to be able to draw them wherever we
38 * like without any copying.
39 * <p>
40 * System applications may retrieve a snapshot to represent the current state of a task, and draw
41 * them in their own process.
42 * <p>
43 * When we task becomes visible again, we show a starting window with the snapshot as the content to
44 * make app transitions more responsive.
45 * <p>
46 * To access this class, acquire the global window manager lock.
47 */
48class TaskSnapshotController {
49
50 private final WindowManagerService mService;
Jorim Jaggi02886a82016-12-06 09:10:06 -080051
Jorim Jaggi7361bab2017-01-16 17:17:58 +010052 private final TaskSnapshotCache mCache;
Jorim Jaggif9084ec2017-01-16 13:16:59 +010053 private final TaskSnapshotPersister mPersister = new TaskSnapshotPersister(
54 Environment::getDataSystemCeDirectory);
55 private final TaskSnapshotLoader mLoader = new TaskSnapshotLoader(mPersister);
Jorim Jaggi02886a82016-12-06 09:10:06 -080056 private final ArraySet<Task> mTmpTasks = new ArraySet<>();
57
58 TaskSnapshotController(WindowManagerService service) {
59 mService = service;
Jorim Jaggi7361bab2017-01-16 17:17:58 +010060 mCache = new TaskSnapshotCache(mService, mLoader);
Jorim Jaggi02886a82016-12-06 09:10:06 -080061 }
62
Jorim Jaggif9084ec2017-01-16 13:16:59 +010063 void systemReady() {
64 mPersister.start();
65 }
66
Jorim Jaggi02886a82016-12-06 09:10:06 -080067 void onTransitionStarting() {
68 if (!ENABLE_TASK_SNAPSHOTS) {
69 return;
70 }
Jorim Jaggi8b702ed2017-01-20 16:59:03 +010071 handleClosingApps(mService.mClosingApps);
72 }
73
74
75 /**
76 * Called when the visibility of an app changes outside of the regular app transition flow.
77 */
78 void notifyAppVisibilityChanged(AppWindowToken appWindowToken, boolean visible) {
79 if (!ENABLE_TASK_SNAPSHOTS) {
80 return;
81 }
82 if (!visible) {
83 handleClosingApps(Sets.newArraySet(appWindowToken));
84 }
85 }
86
87 private void handleClosingApps(ArraySet<AppWindowToken> closingApps) {
Jorim Jaggi02886a82016-12-06 09:10:06 -080088
89 // We need to take a snapshot of the task if and only if all activities of the task are
90 // either closing or hidden.
Jorim Jaggi8b702ed2017-01-20 16:59:03 +010091 getClosingTasks(closingApps, mTmpTasks);
Jorim Jaggi02886a82016-12-06 09:10:06 -080092 for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
93 final Task task = mTmpTasks.valueAt(i);
94 if (!canSnapshotTask(task)) {
95 continue;
96 }
Jorim Jaggie2c77f92016-12-29 14:57:22 +010097 final TaskSnapshot snapshot = snapshotTask(task);
98 if (snapshot != null) {
99 mCache.putSnapshot(task, snapshot);
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100100 mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100101 if (task.getController() != null) {
102 task.getController().reportSnapshotChanged(snapshot);
103 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800104 }
105 }
106 }
107
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100108 /**
109 * Retrieves a snapshot. If {@param restoreFromDisk} equals {@code true}, DO HOLD THE WINDOW
110 * MANAGER LOCK WHEN CALLING THIS METHOD!
111 */
112 @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk) {
113 return mCache.getSnapshot(taskId, userId, restoreFromDisk);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800114 }
115
116 /**
117 * Creates a starting surface for {@param token} with {@param snapshot}. DO NOT HOLD THE WINDOW
118 * MANAGER LOCK WHEN CALLING THIS METHOD!
119 */
120 StartingSurface createStartingSurface(AppWindowToken token,
121 GraphicBuffer snapshot) {
122 return TaskSnapshotSurface.create(mService, token, snapshot);
123 }
124
Jorim Jaggie2c77f92016-12-29 14:57:22 +0100125 private TaskSnapshot snapshotTask(Task task) {
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100126 final AppWindowToken top = task.getTopChild();
Jorim Jaggi02886a82016-12-06 09:10:06 -0800127 if (top == null) {
128 return null;
129 }
Jorim Jaggi6a7a8592017-01-12 00:44:33 +0100130 final GraphicBuffer buffer = top.mDisplayContent.screenshotApplicationsToBuffer(top.token,
131 -1, -1, false, 1.0f, false, true);
132 if (buffer == null) {
Jorim Jaggi02886a82016-12-06 09:10:06 -0800133 return null;
134 }
Jorim Jaggie2c77f92016-12-29 14:57:22 +0100135 return new TaskSnapshot(buffer, top.getConfiguration().orientation,
136 top.findMainWindow().mStableInsets);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800137 }
138
139 /**
140 * Retrieves all closing tasks based on the list of closing apps during an app transition.
141 */
142 @VisibleForTesting
143 void getClosingTasks(ArraySet<AppWindowToken> closingApps, ArraySet<Task> outClosingTasks) {
144 outClosingTasks.clear();
145 for (int i = closingApps.size() - 1; i >= 0; i--) {
146 final AppWindowToken atoken = closingApps.valueAt(i);
147
148 // If the task of the app is not visible anymore, it means no other app in that task
149 // is opening. Thus, the task is closing.
150 if (atoken.mTask != null && !atoken.mTask.isVisible()) {
151 outClosingTasks.add(closingApps.valueAt(i).mTask);
152 }
153 }
154 }
155
156 private boolean canSnapshotTask(Task task) {
157 return !StackId.isHomeOrRecentsStack(task.mStack.mStackId);
158 }
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100159
160 /**
161 * Called when an {@link AppWindowToken} has been removed.
162 */
163 void onAppRemoved(AppWindowToken wtoken) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100164 mCache.onAppRemoved(wtoken);
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100165 }
166
167 /**
168 * Called when the process of an {@link AppWindowToken} has died.
169 */
170 void onAppDied(AppWindowToken wtoken) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100171 mCache.onAppDied(wtoken);
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100172 }
173
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100174 void notifyTaskRemovedFromRecents(int taskId, int userId) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100175 mCache.onTaskRemoved(taskId);
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100176 mPersister.onTaskRemovedFromRecents(taskId, userId);
177 }
178
179 /**
180 * See {@link TaskSnapshotPersister#removeObsoleteFiles}
181 */
182 void removeObsoleteTaskFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) {
183 mPersister.removeObsoleteFiles(persistentTaskIds, runningUserIds);
184 }
185
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100186 void dump(PrintWriter pw, String prefix) {
187 mCache.dump(pw, prefix);
188 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800189}