blob: 6b13edd072cd160f1b17b8b7ff6d63ecee01e766 [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
Matthew Ngcb7ac672017-07-21 17:27:42 -070019import static com.android.server.wm.TaskSnapshotPersister.DISABLE_FULL_SIZED_BITMAPS;
20import static com.android.server.wm.TaskSnapshotPersister.REDUCED_SCALE;
chaviwfbe47df2017-11-10 16:14:49 -080021import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
Winson Chung3e13ef82017-06-29 12:41:14 -070022import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
23import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
24
Jorim Jaggi02886a82016-12-06 09:10:06 -080025import android.annotation.Nullable;
Jorim Jaggi3d732602017-02-22 17:56:40 +010026import android.app.ActivityManager;
Jorim Jaggie2c77f92016-12-29 14:57:22 +010027import android.app.ActivityManager.TaskSnapshot;
Keyvan Amirie681cec2017-06-05 22:48:26 -070028import android.content.pm.PackageManager;
Jorim Jaggi6aead1c2017-05-23 15:07:44 +020029import android.graphics.Bitmap;
Jorim Jaggi02886a82016-12-06 09:10:06 -080030import android.graphics.GraphicBuffer;
Jorim Jaggi30d64f32017-04-07 16:33:17 +020031import android.graphics.Rect;
Jorim Jaggif9084ec2017-01-16 13:16:59 +010032import android.os.Environment;
Jorim Jaggi51304d72017-05-17 17:25:32 +020033import android.os.Handler;
Jorim Jaggi02886a82016-12-06 09:10:06 -080034import android.util.ArraySet;
Winson Chung3e13ef82017-06-29 12:41:14 -070035import android.util.Slog;
Winson Chung91092762017-05-24 14:08:48 -070036import android.view.DisplayListCanvas;
37import android.view.RenderNode;
chaviwfbe47df2017-11-10 16:14:49 -080038import android.view.SurfaceControl;
Winson Chung91092762017-05-24 14:08:48 -070039import android.view.ThreadedRenderer;
Jorim Jaggid635a4a2017-05-03 15:21:26 +020040import android.view.WindowManager.LayoutParams;
Guang Zhu09486a32017-06-06 03:27:44 +000041
Jorim Jaggi02886a82016-12-06 09:10:06 -080042import com.android.internal.annotations.VisibleForTesting;
Winson Chung51f42d22018-02-01 14:59:38 -080043import com.android.internal.graphics.ColorUtils;
Adrian Roose99bc052017-11-20 17:55:31 +010044import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
45import com.android.server.policy.WindowManagerPolicy.StartingSurface;
Jorim Jaggid635a4a2017-05-03 15:21:26 +020046import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter;
Jorim Jaggi817ebdd2018-03-26 15:46:01 +020047import com.android.server.wm.utils.InsetUtils;
Jorim Jaggi02886a82016-12-06 09:10:06 -080048
Adrian Roose99bc052017-11-20 17:55:31 +010049import com.google.android.collect.Sets;
50
Jorim Jaggi10abe2f2017-01-03 16:44:46 +010051import java.io.PrintWriter;
52
Jorim Jaggi02886a82016-12-06 09:10:06 -080053/**
54 * When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
55 * put it into our cache. Internally we use gralloc buffers to be able to draw them wherever we
56 * like without any copying.
57 * <p>
58 * System applications may retrieve a snapshot to represent the current state of a task, and draw
59 * them in their own process.
60 * <p>
61 * When we task becomes visible again, we show a starting window with the snapshot as the content to
62 * make app transitions more responsive.
63 * <p>
64 * To access this class, acquire the global window manager lock.
65 */
66class TaskSnapshotController {
Winson Chung3e13ef82017-06-29 12:41:14 -070067 private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotController" : TAG_WM;
Jorim Jaggi02886a82016-12-06 09:10:06 -080068
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +010069 /**
70 * Return value for {@link #getSnapshotMode}: We are allowed to take a real screenshot to be
71 * used as the snapshot.
72 */
73 @VisibleForTesting
74 static final int SNAPSHOT_MODE_REAL = 0;
75
76 /**
77 * Return value for {@link #getSnapshotMode}: We are not allowed to take a real screenshot but
78 * we should try to use the app theme to create a dummy representation of the app.
79 */
80 @VisibleForTesting
81 static final int SNAPSHOT_MODE_APP_THEME = 1;
82
83 /**
84 * Return value for {@link #getSnapshotMode}: We aren't allowed to take any snapshot.
85 */
86 @VisibleForTesting
87 static final int SNAPSHOT_MODE_NONE = 2;
88
Jorim Jaggi02886a82016-12-06 09:10:06 -080089 private final WindowManagerService mService;
Jorim Jaggi02886a82016-12-06 09:10:06 -080090
Jorim Jaggi7361bab2017-01-16 17:17:58 +010091 private final TaskSnapshotCache mCache;
Jorim Jaggif9084ec2017-01-16 13:16:59 +010092 private final TaskSnapshotPersister mPersister = new TaskSnapshotPersister(
93 Environment::getDataSystemCeDirectory);
94 private final TaskSnapshotLoader mLoader = new TaskSnapshotLoader(mPersister);
Winson Chung23aa7b12018-02-01 11:41:43 -080095 private final ArraySet<Task> mSkipClosingAppSnapshotTasks = new ArraySet<>();
Jorim Jaggi02886a82016-12-06 09:10:06 -080096 private final ArraySet<Task> mTmpTasks = new ArraySet<>();
Jorim Jaggi51304d72017-05-17 17:25:32 +020097 private final Handler mHandler = new Handler();
Jorim Jaggi02886a82016-12-06 09:10:06 -080098
chaviw23ee71c2017-12-18 11:29:41 -080099 private final Rect mTmpRect = new Rect();
100
Keyvan Amirie681cec2017-06-05 22:48:26 -0700101 /**
102 * Flag indicating whether we are running on an Android TV device.
103 */
104 private final boolean mIsRunningOnTv;
105
Juho Haf5dd7cc2017-06-15 11:38:56 +0900106 /**
107 * Flag indicating whether we are running on an IoT device.
108 */
109 private final boolean mIsRunningOnIoT;
110
Matthew Ng95378192017-08-16 11:57:00 -0700111 /**
112 * Flag indicating whether we are running on an Android Wear device.
113 */
114 private final boolean mIsRunningOnWear;
115
Jorim Jaggi02886a82016-12-06 09:10:06 -0800116 TaskSnapshotController(WindowManagerService service) {
117 mService = service;
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100118 mCache = new TaskSnapshotCache(mService, mLoader);
Keyvan Amirie681cec2017-06-05 22:48:26 -0700119 mIsRunningOnTv = mService.mContext.getPackageManager().hasSystemFeature(
120 PackageManager.FEATURE_LEANBACK);
Juho Haf5dd7cc2017-06-15 11:38:56 +0900121 mIsRunningOnIoT = mService.mContext.getPackageManager().hasSystemFeature(
122 PackageManager.FEATURE_EMBEDDED);
Matthew Ng95378192017-08-16 11:57:00 -0700123 mIsRunningOnWear = mService.mContext.getPackageManager().hasSystemFeature(
124 PackageManager.FEATURE_WATCH);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800125 }
126
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100127 void systemReady() {
128 mPersister.start();
129 }
130
Jorim Jaggi02886a82016-12-06 09:10:06 -0800131 void onTransitionStarting() {
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100132 handleClosingApps(mService.mClosingApps);
133 }
134
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100135 /**
136 * Called when the visibility of an app changes outside of the regular app transition flow.
137 */
138 void notifyAppVisibilityChanged(AppWindowToken appWindowToken, boolean visible) {
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100139 if (!visible) {
140 handleClosingApps(Sets.newArraySet(appWindowToken));
141 }
142 }
143
144 private void handleClosingApps(ArraySet<AppWindowToken> closingApps) {
Keyvan Amirie681cec2017-06-05 22:48:26 -0700145 if (shouldDisableSnapshots()) {
Jorim Jaggi3d732602017-02-22 17:56:40 +0100146 return;
147 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800148
149 // We need to take a snapshot of the task if and only if all activities of the task are
150 // either closing or hidden.
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100151 getClosingTasks(closingApps, mTmpTasks);
Jorim Jaggi51304d72017-05-17 17:25:32 +0200152 snapshotTasks(mTmpTasks);
Winson Chung23aa7b12018-02-01 11:41:43 -0800153 mSkipClosingAppSnapshotTasks.clear();
Jorim Jaggi51304d72017-05-17 17:25:32 +0200154 }
155
Winson Chung23aa7b12018-02-01 11:41:43 -0800156 /**
157 * Adds the given {@param tasks} to the list of tasks which should not have their snapshots
158 * taken upon the next processing of the set of closing apps. The caller is responsible for
159 * calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot.
160 */
161 @VisibleForTesting
162 void addSkipClosingAppSnapshotTasks(ArraySet<Task> tasks) {
163 mSkipClosingAppSnapshotTasks.addAll(tasks);
164 }
165
166 void snapshotTasks(ArraySet<Task> tasks) {
Jorim Jaggi51304d72017-05-17 17:25:32 +0200167 for (int i = tasks.size() - 1; i >= 0; i--) {
168 final Task task = tasks.valueAt(i);
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100169 final int mode = getSnapshotMode(task);
170 final TaskSnapshot snapshot;
171 switch (mode) {
172 case SNAPSHOT_MODE_NONE:
173 continue;
174 case SNAPSHOT_MODE_APP_THEME:
175 snapshot = drawAppThemeSnapshot(task);
176 break;
177 case SNAPSHOT_MODE_REAL:
178 snapshot = snapshotTask(task);
179 break;
180 default:
181 snapshot = null;
182 break;
Jorim Jaggi02886a82016-12-06 09:10:06 -0800183 }
Jorim Jaggie2c77f92016-12-29 14:57:22 +0100184 if (snapshot != null) {
Winson Chung3e13ef82017-06-29 12:41:14 -0700185 final GraphicBuffer buffer = snapshot.getSnapshot();
186 if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
187 buffer.destroy();
188 Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
189 + buffer.getHeight());
190 } else {
191 mCache.putSnapshot(task, snapshot);
192 mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
193 if (task.getController() != null) {
194 task.getController().reportSnapshotChanged(snapshot);
195 }
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100196 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800197 }
198 }
199 }
200
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100201 /**
202 * Retrieves a snapshot. If {@param restoreFromDisk} equals {@code true}, DO HOLD THE WINDOW
203 * MANAGER LOCK WHEN CALLING THIS METHOD!
204 */
Jorim Jaggi35e3f532017-03-17 17:06:50 +0100205 @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
206 boolean reducedResolution) {
Matthew Ngcb7ac672017-07-21 17:27:42 -0700207 return mCache.getSnapshot(taskId, userId, restoreFromDisk, reducedResolution
208 || DISABLE_FULL_SIZED_BITMAPS);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800209 }
210
211 /**
212 * Creates a starting surface for {@param token} with {@param snapshot}. DO NOT HOLD THE WINDOW
213 * MANAGER LOCK WHEN CALLING THIS METHOD!
214 */
215 StartingSurface createStartingSurface(AppWindowToken token,
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200216 TaskSnapshot snapshot) {
Jorim Jaggi02886a82016-12-06 09:10:06 -0800217 return TaskSnapshotSurface.create(mService, token, snapshot);
218 }
219
Jorim Jaggie2c77f92016-12-29 14:57:22 +0100220 private TaskSnapshot snapshotTask(Task task) {
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100221 final AppWindowToken top = task.getTopChild();
Jorim Jaggi02886a82016-12-06 09:10:06 -0800222 if (top == null) {
223 return null;
224 }
Jorim Jaggidd5986e2017-04-11 15:50:19 -0700225 final WindowState mainWindow = top.findMainWindow();
226 if (mainWindow == null) {
227 return null;
228 }
chaviwfbe47df2017-11-10 16:14:49 -0800229 if (!mService.mPolicy.isScreenOn()) {
230 if (DEBUG_SCREENSHOT) {
231 Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
232 }
233 return null;
234 }
235 if (task.getSurfaceControl() == null) {
236 return null;
237 }
238
chaviw7f1fa992018-01-10 13:52:12 -0800239 if (top.hasCommittedReparentToAnimationLeash()) {
240 if (DEBUG_SCREENSHOT) {
241 Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + top);
242 }
243 return null;
244 }
245
246 final boolean hasVisibleChild = top.forAllWindows(
247 // Ensure at least one window for the top app is visible before attempting to take
248 // a screenshot. Visible here means that the WSA surface is shown and has an alpha
249 // greater than 0.
Jorim Jaggi77d0f36c2018-03-16 17:49:49 +0100250 ws -> (ws.mAppToken == null || ws.mAppToken.isSurfaceShowing())
251 && ws.mWinAnimator != null && ws.mWinAnimator.getShown()
chaviw7f1fa992018-01-10 13:52:12 -0800252 && ws.mWinAnimator.mLastAlpha > 0f, true);
253
254 if (!hasVisibleChild) {
255 if (DEBUG_SCREENSHOT) {
256 Slog.w(TAG_WM, "Failed to take screenshot. No visible windows for " + task);
257 }
258 return null;
259 }
260
Matthew Ngcb7ac672017-07-21 17:27:42 -0700261 final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
262 final float scaleFraction = isLowRamDevice ? REDUCED_SCALE : 1f;
chaviw23ee71c2017-12-18 11:29:41 -0800263 task.getBounds(mTmpRect);
264 mTmpRect.offsetTo(0, 0);
chaviwfbe47df2017-11-10 16:14:49 -0800265
Chavi Weingartend7ec64c2017-11-30 01:52:01 +0000266 final GraphicBuffer buffer = SurfaceControl.captureLayers(
chaviw23ee71c2017-12-18 11:29:41 -0800267 task.getSurfaceControl().getHandle(), mTmpRect, scaleFraction);
chaviwfbe47df2017-11-10 16:14:49 -0800268
Juho Had864b442017-06-12 20:15:31 +0900269 if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
chaviwfbe47df2017-11-10 16:14:49 -0800270 if (DEBUG_SCREENSHOT) {
chaviw7f1fa992018-01-10 13:52:12 -0800271 Slog.w(TAG_WM, "Failed to take screenshot for " + task);
chaviwfbe47df2017-11-10 16:14:49 -0800272 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800273 return null;
274 }
Jorim Jaggie2c77f92016-12-29 14:57:22 +0100275 return new TaskSnapshot(buffer, top.getConfiguration().orientation,
Winson Chunga4fa8d52018-04-20 15:54:51 -0700276 getInsets(mainWindow), isLowRamDevice /* reduced */, scaleFraction /* scale */,
277 true /* isRealSnapshot */, task.getWindowingMode());
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200278 }
279
Keyvan Amirie681cec2017-06-05 22:48:26 -0700280 private boolean shouldDisableSnapshots() {
Jorim Jaggie7d2b852017-08-28 17:55:15 +0200281 return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
Keyvan Amirie681cec2017-06-05 22:48:26 -0700282 }
283
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200284 private Rect getInsets(WindowState state) {
Adrian Roos20e07892018-02-23 19:12:01 +0100285 // XXX(b/72757033): These are insets relative to the window frame, but we're really
286 // interested in the insets relative to the task bounds.
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200287 final Rect insets = minRect(state.mContentInsets, state.mStableInsets);
288 InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets());
Adrian Roos20e07892018-02-23 19:12:01 +0100289 return insets;
290 }
Adrian Roos98a146d2017-11-29 16:39:44 +0100291
Adrian Roos20e07892018-02-23 19:12:01 +0100292 private Rect minRect(Rect rect1, Rect rect2) {
293 return new Rect(Math.min(rect1.left, rect2.left),
294 Math.min(rect1.top, rect2.top),
295 Math.min(rect1.right, rect2.right),
296 Math.min(rect1.bottom, rect2.bottom));
297 }
Adrian Roos98a146d2017-11-29 16:39:44 +0100298
Jorim Jaggi02886a82016-12-06 09:10:06 -0800299 /**
300 * Retrieves all closing tasks based on the list of closing apps during an app transition.
301 */
302 @VisibleForTesting
303 void getClosingTasks(ArraySet<AppWindowToken> closingApps, ArraySet<Task> outClosingTasks) {
304 outClosingTasks.clear();
305 for (int i = closingApps.size() - 1; i >= 0; i--) {
306 final AppWindowToken atoken = closingApps.valueAt(i);
Bryce Lee6d410262017-02-28 15:30:17 -0800307 final Task task = atoken.getTask();
Jorim Jaggi02886a82016-12-06 09:10:06 -0800308
309 // If the task of the app is not visible anymore, it means no other app in that task
310 // is opening. Thus, the task is closing.
Winson Chung23aa7b12018-02-01 11:41:43 -0800311 if (task != null && !task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
Bryce Lee6d410262017-02-28 15:30:17 -0800312 outClosingTasks.add(task);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800313 }
314 }
315 }
316
Jorim Jaggi0fe7ce962017-02-22 16:45:48 +0100317 @VisibleForTesting
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100318 int getSnapshotMode(Task task) {
Jorim Jaggi0fe7ce962017-02-22 16:45:48 +0100319 final AppWindowToken topChild = task.getTopChild();
Wale Ogunwale68278562017-09-23 17:13:55 -0700320 if (!task.isActivityTypeStandardOrUndefined()) {
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100321 return SNAPSHOT_MODE_NONE;
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200322 } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100323 return SNAPSHOT_MODE_APP_THEME;
324 } else {
325 return SNAPSHOT_MODE_REAL;
326 }
327 }
328
329 /**
330 * If we are not allowed to take a real screenshot, this attempts to represent the app as best
331 * as possible by using the theme's window background.
332 */
333 private TaskSnapshot drawAppThemeSnapshot(Task task) {
334 final AppWindowToken topChild = task.getTopChild();
335 if (topChild == null) {
336 return null;
337 }
338 final WindowState mainWindow = topChild.findMainWindow();
339 if (mainWindow == null) {
340 return null;
341 }
Winson Chung51f42d22018-02-01 14:59:38 -0800342 final int color = ColorUtils.setAlphaComponent(
343 task.getTaskDescription().getBackgroundColor(), 255);
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200344 final int statusBarColor = task.getTaskDescription().getStatusBarColor();
345 final int navigationBarColor = task.getTaskDescription().getNavigationBarColor();
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200346 final LayoutParams attrs = mainWindow.getAttrs();
347 final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
348 attrs.privateFlags, attrs.systemUiVisibility, statusBarColor, navigationBarColor);
Winson Chung91092762017-05-24 14:08:48 -0700349 final int width = mainWindow.getFrameLw().width();
350 final int height = mainWindow.getFrameLw().height();
351
352 final RenderNode node = RenderNode.create("TaskSnapshotController", null);
353 node.setLeftTopRightBottom(0, 0, width, height);
354 node.setClipToBounds(false);
355 final DisplayListCanvas c = node.start(width, height);
356 c.drawColor(color);
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200357 decorPainter.setInsets(mainWindow.mContentInsets, mainWindow.mStableInsets);
358 decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
Winson Chung91092762017-05-24 14:08:48 -0700359 node.end(c);
360 final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
Winson Chung6267f992017-10-06 14:01:10 -0700361 if (hwBitmap == null) {
362 return null;
363 }
Jorim Jaggi6aead1c2017-05-23 15:07:44 +0200364 return new TaskSnapshot(hwBitmap.createGraphicBufferHandle(),
365 topChild.getConfiguration().orientation, mainWindow.mStableInsets,
Winson Chungf3e412e2018-03-08 11:07:40 -0800366 ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */,
Winson Chunga4fa8d52018-04-20 15:54:51 -0700367 false /* isRealSnapshot */, task.getWindowingMode());
Jorim Jaggi02886a82016-12-06 09:10:06 -0800368 }
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100369
370 /**
371 * Called when an {@link AppWindowToken} has been removed.
372 */
373 void onAppRemoved(AppWindowToken wtoken) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100374 mCache.onAppRemoved(wtoken);
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100375 }
376
377 /**
378 * Called when the process of an {@link AppWindowToken} has died.
379 */
380 void onAppDied(AppWindowToken wtoken) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100381 mCache.onAppDied(wtoken);
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100382 }
383
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100384 void notifyTaskRemovedFromRecents(int taskId, int userId) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100385 mCache.onTaskRemoved(taskId);
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100386 mPersister.onTaskRemovedFromRecents(taskId, userId);
387 }
388
389 /**
390 * See {@link TaskSnapshotPersister#removeObsoleteFiles}
391 */
392 void removeObsoleteTaskFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) {
393 mPersister.removeObsoleteFiles(persistentTaskIds, runningUserIds);
394 }
395
Jorim Jaggia41b7292017-05-11 23:50:34 +0200396 /**
397 * Temporarily pauses/unpauses persisting of task snapshots.
398 *
399 * @param paused Whether task snapshot persisting should be paused.
400 */
401 void setPersisterPaused(boolean paused) {
402 mPersister.setPaused(paused);
403 }
404
Jorim Jaggi51304d72017-05-17 17:25:32 +0200405 /**
406 * Called when screen is being turned off.
407 */
408 void screenTurningOff(ScreenOffListener listener) {
Keyvan Amirie681cec2017-06-05 22:48:26 -0700409 if (shouldDisableSnapshots()) {
Jorim Jaggi51304d72017-05-17 17:25:32 +0200410 listener.onScreenOff();
411 return;
412 }
413
414 // We can't take a snapshot when screen is off, so take a snapshot now!
415 mHandler.post(() -> {
416 try {
417 synchronized (mService.mWindowMap) {
418 mTmpTasks.clear();
419 mService.mRoot.forAllTasks(task -> {
420 if (task.isVisible()) {
421 mTmpTasks.add(task);
422 }
423 });
424 snapshotTasks(mTmpTasks);
425 }
426 } finally {
427 listener.onScreenOff();
428 }
429 });
430 }
431
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100432 void dump(PrintWriter pw, String prefix) {
433 mCache.dump(pw, prefix);
434 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800435}