blob: beb3d82c05fb69a448466f9a23eff7a22254e8f6 [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;
chaviwfbe47df2017-11-10 16:14:49 -080020import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
Winson Chung3e13ef82017-06-29 12:41:14 -070021import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
22import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
23
Jorim Jaggi02886a82016-12-06 09:10:06 -080024import android.annotation.Nullable;
Jorim Jaggi3d732602017-02-22 17:56:40 +010025import android.app.ActivityManager;
Jorim Jaggie2c77f92016-12-29 14:57:22 +010026import android.app.ActivityManager.TaskSnapshot;
Keyvan Amirie681cec2017-06-05 22:48:26 -070027import android.content.pm.PackageManager;
Jorim Jaggi6aead1c2017-05-23 15:07:44 +020028import android.graphics.Bitmap;
Jorim Jaggi02886a82016-12-06 09:10:06 -080029import android.graphics.GraphicBuffer;
Winson Chung02378b42018-06-04 16:40:09 -070030import android.graphics.PixelFormat;
John Reck32f140aa62018-10-04 15:08:24 -070031import android.graphics.RecordingCanvas;
Jorim Jaggi30d64f32017-04-07 16:33:17 +020032import android.graphics.Rect;
John Reck32f140aa62018-10-04 15:08:24 -070033import android.graphics.RenderNode;
Jorim Jaggif9084ec2017-01-16 13:16:59 +010034import android.os.Environment;
Jorim Jaggi51304d72017-05-17 17:25:32 +020035import android.os.Handler;
Jorim Jaggi02886a82016-12-06 09:10:06 -080036import android.util.ArraySet;
Winson Chung3e13ef82017-06-29 12:41:14 -070037import android.util.Slog;
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;
Kevin716a9db2018-12-18 16:53:39 -080092 private final TaskSnapshotPersister mPersister;
93 private final TaskSnapshotLoader mLoader;
Winson Chung23aa7b12018-02-01 11:41:43 -080094 private final ArraySet<Task> mSkipClosingAppSnapshotTasks = new ArraySet<>();
Jorim Jaggi02886a82016-12-06 09:10:06 -080095 private final ArraySet<Task> mTmpTasks = new ArraySet<>();
Jorim Jaggi51304d72017-05-17 17:25:32 +020096 private final Handler mHandler = new Handler();
Jorim Jaggi02886a82016-12-06 09:10:06 -080097
chaviw23ee71c2017-12-18 11:29:41 -080098 private final Rect mTmpRect = new Rect();
99
Keyvan Amirie681cec2017-06-05 22:48:26 -0700100 /**
101 * Flag indicating whether we are running on an Android TV device.
102 */
103 private final boolean mIsRunningOnTv;
104
Juho Haf5dd7cc2017-06-15 11:38:56 +0900105 /**
106 * Flag indicating whether we are running on an IoT device.
107 */
108 private final boolean mIsRunningOnIoT;
109
Matthew Ng95378192017-08-16 11:57:00 -0700110 /**
111 * Flag indicating whether we are running on an Android Wear device.
112 */
113 private final boolean mIsRunningOnWear;
114
Jorim Jaggi02886a82016-12-06 09:10:06 -0800115 TaskSnapshotController(WindowManagerService service) {
116 mService = service;
Kevin716a9db2018-12-18 16:53:39 -0800117 mPersister = new TaskSnapshotPersister(mService, Environment::getDataSystemCeDirectory);
118 mLoader = new TaskSnapshotLoader(mPersister);
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100119 mCache = new TaskSnapshotCache(mService, mLoader);
Keyvan Amirie681cec2017-06-05 22:48:26 -0700120 mIsRunningOnTv = mService.mContext.getPackageManager().hasSystemFeature(
121 PackageManager.FEATURE_LEANBACK);
Juho Haf5dd7cc2017-06-15 11:38:56 +0900122 mIsRunningOnIoT = mService.mContext.getPackageManager().hasSystemFeature(
123 PackageManager.FEATURE_EMBEDDED);
Matthew Ng95378192017-08-16 11:57:00 -0700124 mIsRunningOnWear = mService.mContext.getPackageManager().hasSystemFeature(
125 PackageManager.FEATURE_WATCH);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800126 }
127
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100128 void systemReady() {
129 mPersister.start();
130 }
131
lumark588a3e82018-07-20 18:53:54 +0800132 void onTransitionStarting(DisplayContent displayContent) {
133 handleClosingApps(displayContent.mClosingApps);
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100134 }
135
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100136 /**
137 * Called when the visibility of an app changes outside of the regular app transition flow.
138 */
139 void notifyAppVisibilityChanged(AppWindowToken appWindowToken, boolean visible) {
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100140 if (!visible) {
141 handleClosingApps(Sets.newArraySet(appWindowToken));
142 }
143 }
144
145 private void handleClosingApps(ArraySet<AppWindowToken> closingApps) {
Keyvan Amirie681cec2017-06-05 22:48:26 -0700146 if (shouldDisableSnapshots()) {
Jorim Jaggi3d732602017-02-22 17:56:40 +0100147 return;
148 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800149
150 // We need to take a snapshot of the task if and only if all activities of the task are
151 // either closing or hidden.
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100152 getClosingTasks(closingApps, mTmpTasks);
Jorim Jaggi51304d72017-05-17 17:25:32 +0200153 snapshotTasks(mTmpTasks);
Winson Chung23aa7b12018-02-01 11:41:43 -0800154 mSkipClosingAppSnapshotTasks.clear();
Jorim Jaggi51304d72017-05-17 17:25:32 +0200155 }
156
Winson Chung23aa7b12018-02-01 11:41:43 -0800157 /**
158 * Adds the given {@param tasks} to the list of tasks which should not have their snapshots
159 * taken upon the next processing of the set of closing apps. The caller is responsible for
160 * calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot.
161 */
162 @VisibleForTesting
163 void addSkipClosingAppSnapshotTasks(ArraySet<Task> tasks) {
164 mSkipClosingAppSnapshotTasks.addAll(tasks);
165 }
166
167 void snapshotTasks(ArraySet<Task> tasks) {
Jorim Jaggi51304d72017-05-17 17:25:32 +0200168 for (int i = tasks.size() - 1; i >= 0; i--) {
169 final Task task = tasks.valueAt(i);
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100170 final int mode = getSnapshotMode(task);
171 final TaskSnapshot snapshot;
172 switch (mode) {
173 case SNAPSHOT_MODE_NONE:
174 continue;
175 case SNAPSHOT_MODE_APP_THEME:
176 snapshot = drawAppThemeSnapshot(task);
177 break;
178 case SNAPSHOT_MODE_REAL:
179 snapshot = snapshotTask(task);
180 break;
181 default:
182 snapshot = null;
183 break;
Jorim Jaggi02886a82016-12-06 09:10:06 -0800184 }
Jorim Jaggie2c77f92016-12-29 14:57:22 +0100185 if (snapshot != null) {
Winson Chung3e13ef82017-06-29 12:41:14 -0700186 final GraphicBuffer buffer = snapshot.getSnapshot();
187 if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
188 buffer.destroy();
189 Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
190 + buffer.getHeight());
191 } else {
192 mCache.putSnapshot(task, snapshot);
193 mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
Yunfan Chen0e7aff92018-12-05 16:35:32 -0800194 task.onSnapshotChanged(snapshot);
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100195 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800196 }
197 }
198 }
199
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100200 /**
201 * Retrieves a snapshot. If {@param restoreFromDisk} equals {@code true}, DO HOLD THE WINDOW
202 * MANAGER LOCK WHEN CALLING THIS METHOD!
203 */
Jorim Jaggi35e3f532017-03-17 17:06:50 +0100204 @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
205 boolean reducedResolution) {
Matthew Ngcb7ac672017-07-21 17:27:42 -0700206 return mCache.getSnapshot(taskId, userId, restoreFromDisk, reducedResolution
207 || DISABLE_FULL_SIZED_BITMAPS);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800208 }
209
210 /**
211 * Creates a starting surface for {@param token} with {@param snapshot}. DO NOT HOLD THE WINDOW
212 * MANAGER LOCK WHEN CALLING THIS METHOD!
213 */
214 StartingSurface createStartingSurface(AppWindowToken token,
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200215 TaskSnapshot snapshot) {
Jorim Jaggi02886a82016-12-06 09:10:06 -0800216 return TaskSnapshotSurface.create(mService, token, snapshot);
217 }
218
Issei Suzuki88e21422018-07-24 16:29:09 +0200219 /**
220 * Find the window for a given task to take a snapshot. Top child of the task is usually the one
221 * we're looking for, but during app transitions, trampoline activities can appear in the
222 * children, which should be ignored.
223 */
224 @Nullable private AppWindowToken findAppTokenForSnapshot(Task task) {
225 for (int i = task.getChildCount() - 1; i >= 0; --i) {
226 final AppWindowToken appWindowToken = task.getChildAt(i);
227 if (appWindowToken == null || !appWindowToken.isSurfaceShowing()
228 || appWindowToken.findMainWindow() == null) {
229 continue;
230 }
231 final boolean hasVisibleChild = appWindowToken.forAllWindows(
232 // Ensure at least one window for the top app is visible before attempting to
233 // take a screenshot. Visible here means that the WSA surface is shown and has
234 // an alpha greater than 0.
235 ws -> ws.mWinAnimator != null && ws.mWinAnimator.getShown()
236 && ws.mWinAnimator.mLastAlpha > 0f, true /* traverseTopToBottom */);
237 if (hasVisibleChild) {
238 return appWindowToken;
239 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800240 }
Issei Suzuki88e21422018-07-24 16:29:09 +0200241 return null;
242 }
243
244 @Nullable private TaskSnapshot snapshotTask(Task task) {
chaviwfbe47df2017-11-10 16:14:49 -0800245 if (!mService.mPolicy.isScreenOn()) {
246 if (DEBUG_SCREENSHOT) {
247 Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
248 }
249 return null;
250 }
251 if (task.getSurfaceControl() == null) {
chaviw7f1fa992018-01-10 13:52:12 -0800252 if (DEBUG_SCREENSHOT) {
Issei Suzuki88e21422018-07-24 16:29:09 +0200253 Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
chaviw7f1fa992018-01-10 13:52:12 -0800254 }
255 return null;
256 }
257
Issei Suzuki88e21422018-07-24 16:29:09 +0200258 final AppWindowToken appWindowToken = findAppTokenForSnapshot(task);
259 if (appWindowToken == null) {
chaviw7f1fa992018-01-10 13:52:12 -0800260 if (DEBUG_SCREENSHOT) {
261 Slog.w(TAG_WM, "Failed to take screenshot. No visible windows for " + task);
262 }
263 return null;
264 }
Issei Suzuki88e21422018-07-24 16:29:09 +0200265 if (appWindowToken.hasCommittedReparentToAnimationLeash()) {
266 if (DEBUG_SCREENSHOT) {
267 Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + appWindowToken);
268 }
269 return null;
270 }
chaviw7f1fa992018-01-10 13:52:12 -0800271
Matthew Ngcb7ac672017-07-21 17:27:42 -0700272 final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
Kevin716a9db2018-12-18 16:53:39 -0800273 final float scaleFraction = isLowRamDevice ? mPersister.getReducedScale() : 1f;
chaviw23ee71c2017-12-18 11:29:41 -0800274 task.getBounds(mTmpRect);
275 mTmpRect.offsetTo(0, 0);
chaviwfbe47df2017-11-10 16:14:49 -0800276
Issei Suzuki88e21422018-07-24 16:29:09 +0200277 final WindowState mainWindow = appWindowToken.findMainWindow();
278 if (mainWindow == null) {
279 Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task);
280 return null;
281 }
Peiyong Linbf9d6772019-03-21 20:53:42 +0000282 final GraphicBuffer buffer = SurfaceControl.captureLayers(
283 task.getSurfaceControl().getHandle(), mTmpRect, scaleFraction);
Juho Had864b442017-06-12 20:15:31 +0900284 if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
chaviwfbe47df2017-11-10 16:14:49 -0800285 if (DEBUG_SCREENSHOT) {
chaviw7f1fa992018-01-10 13:52:12 -0800286 Slog.w(TAG_WM, "Failed to take screenshot for " + task);
chaviwfbe47df2017-11-10 16:14:49 -0800287 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800288 return null;
289 }
Issei Suzuki88e21422018-07-24 16:29:09 +0200290 final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
Winson Chung48b25652018-10-22 14:04:30 -0700291 return new TaskSnapshot(appWindowToken.mActivityComponent, buffer,
292 appWindowToken.getConfiguration().orientation, getInsets(mainWindow),
293 isLowRamDevice /* reduced */, scaleFraction /* scale */, true /* isRealSnapshot */,
294 task.getWindowingMode(), getSystemUiVisibility(task),
Issei Suzuki88e21422018-07-24 16:29:09 +0200295 !appWindowToken.fillsParent() || isWindowTranslucent);
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200296 }
297
Keyvan Amirie681cec2017-06-05 22:48:26 -0700298 private boolean shouldDisableSnapshots() {
Jorim Jaggie7d2b852017-08-28 17:55:15 +0200299 return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
Keyvan Amirie681cec2017-06-05 22:48:26 -0700300 }
301
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200302 private Rect getInsets(WindowState state) {
Adrian Roos20e07892018-02-23 19:12:01 +0100303 // XXX(b/72757033): These are insets relative to the window frame, but we're really
304 // interested in the insets relative to the task bounds.
chaviw9c81e632018-07-31 11:17:52 -0700305 final Rect insets = minRect(state.getContentInsets(), state.getStableInsets());
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200306 InsetUtils.addInsets(insets, state.mAppToken.getLetterboxInsets());
Adrian Roos20e07892018-02-23 19:12:01 +0100307 return insets;
308 }
Adrian Roos98a146d2017-11-29 16:39:44 +0100309
Adrian Roos20e07892018-02-23 19:12:01 +0100310 private Rect minRect(Rect rect1, Rect rect2) {
311 return new Rect(Math.min(rect1.left, rect2.left),
312 Math.min(rect1.top, rect2.top),
313 Math.min(rect1.right, rect2.right),
314 Math.min(rect1.bottom, rect2.bottom));
315 }
Adrian Roos98a146d2017-11-29 16:39:44 +0100316
Jorim Jaggi02886a82016-12-06 09:10:06 -0800317 /**
318 * Retrieves all closing tasks based on the list of closing apps during an app transition.
319 */
320 @VisibleForTesting
321 void getClosingTasks(ArraySet<AppWindowToken> closingApps, ArraySet<Task> outClosingTasks) {
322 outClosingTasks.clear();
323 for (int i = closingApps.size() - 1; i >= 0; i--) {
324 final AppWindowToken atoken = closingApps.valueAt(i);
Bryce Lee6d410262017-02-28 15:30:17 -0800325 final Task task = atoken.getTask();
Jorim Jaggi02886a82016-12-06 09:10:06 -0800326
327 // If the task of the app is not visible anymore, it means no other app in that task
328 // is opening. Thus, the task is closing.
Winson Chung23aa7b12018-02-01 11:41:43 -0800329 if (task != null && !task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
Bryce Lee6d410262017-02-28 15:30:17 -0800330 outClosingTasks.add(task);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800331 }
332 }
333 }
334
Jorim Jaggi0fe7ce962017-02-22 16:45:48 +0100335 @VisibleForTesting
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100336 int getSnapshotMode(Task task) {
Jorim Jaggi0fe7ce962017-02-22 16:45:48 +0100337 final AppWindowToken topChild = task.getTopChild();
Winson Chungbff18372018-04-27 16:12:54 -0700338 if (!task.isActivityTypeStandardOrUndefined() && !task.isActivityTypeAssistant()) {
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100339 return SNAPSHOT_MODE_NONE;
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200340 } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100341 return SNAPSHOT_MODE_APP_THEME;
342 } else {
343 return SNAPSHOT_MODE_REAL;
344 }
345 }
346
347 /**
348 * If we are not allowed to take a real screenshot, this attempts to represent the app as best
349 * as possible by using the theme's window background.
350 */
351 private TaskSnapshot drawAppThemeSnapshot(Task task) {
352 final AppWindowToken topChild = task.getTopChild();
353 if (topChild == null) {
354 return null;
355 }
356 final WindowState mainWindow = topChild.findMainWindow();
357 if (mainWindow == null) {
358 return null;
359 }
Winson Chung51f42d22018-02-01 14:59:38 -0800360 final int color = ColorUtils.setAlphaComponent(
361 task.getTaskDescription().getBackgroundColor(), 255);
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200362 final int statusBarColor = task.getTaskDescription().getStatusBarColor();
363 final int navigationBarColor = task.getTaskDescription().getNavigationBarColor();
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200364 final LayoutParams attrs = mainWindow.getAttrs();
365 final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
366 attrs.privateFlags, attrs.systemUiVisibility, statusBarColor, navigationBarColor);
Winson Chung91092762017-05-24 14:08:48 -0700367 final int width = mainWindow.getFrameLw().width();
368 final int height = mainWindow.getFrameLw().height();
369
370 final RenderNode node = RenderNode.create("TaskSnapshotController", null);
371 node.setLeftTopRightBottom(0, 0, width, height);
372 node.setClipToBounds(false);
John Reck32f140aa62018-10-04 15:08:24 -0700373 final RecordingCanvas c = node.start(width, height);
Winson Chung91092762017-05-24 14:08:48 -0700374 c.drawColor(color);
chaviw9c81e632018-07-31 11:17:52 -0700375 decorPainter.setInsets(mainWindow.getContentInsets(), mainWindow.getStableInsets());
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200376 decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
Winson Chung91092762017-05-24 14:08:48 -0700377 node.end(c);
378 final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
Winson Chung6267f992017-10-06 14:01:10 -0700379 if (hwBitmap == null) {
380 return null;
381 }
Winson Chung02378b42018-06-04 16:40:09 -0700382 // Note, the app theme snapshot is never translucent because we enforce a non-translucent
383 // color above
Winson Chung48b25652018-10-22 14:04:30 -0700384 return new TaskSnapshot(topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
chaviw9c81e632018-07-31 11:17:52 -0700385 topChild.getConfiguration().orientation, mainWindow.getStableInsets(),
Winson Chungf3e412e2018-03-08 11:07:40 -0800386 ActivityManager.isLowRamDeviceStatic() /* reduced */, 1.0f /* scale */,
Winson Chung173020c2018-05-04 15:36:47 -0700387 false /* isRealSnapshot */, task.getWindowingMode(), getSystemUiVisibility(task),
Winson Chung02378b42018-06-04 16:40:09 -0700388 false);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800389 }
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100390
391 /**
392 * Called when an {@link AppWindowToken} has been removed.
393 */
394 void onAppRemoved(AppWindowToken wtoken) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100395 mCache.onAppRemoved(wtoken);
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100396 }
397
398 /**
399 * Called when the process of an {@link AppWindowToken} has died.
400 */
401 void onAppDied(AppWindowToken wtoken) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100402 mCache.onAppDied(wtoken);
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100403 }
404
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100405 void notifyTaskRemovedFromRecents(int taskId, int userId) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100406 mCache.onTaskRemoved(taskId);
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100407 mPersister.onTaskRemovedFromRecents(taskId, userId);
408 }
409
410 /**
411 * See {@link TaskSnapshotPersister#removeObsoleteFiles}
412 */
413 void removeObsoleteTaskFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) {
414 mPersister.removeObsoleteFiles(persistentTaskIds, runningUserIds);
415 }
416
Jorim Jaggia41b7292017-05-11 23:50:34 +0200417 /**
418 * Temporarily pauses/unpauses persisting of task snapshots.
419 *
420 * @param paused Whether task snapshot persisting should be paused.
421 */
422 void setPersisterPaused(boolean paused) {
423 mPersister.setPaused(paused);
424 }
425
Jorim Jaggi51304d72017-05-17 17:25:32 +0200426 /**
427 * Called when screen is being turned off.
428 */
429 void screenTurningOff(ScreenOffListener listener) {
Keyvan Amirie681cec2017-06-05 22:48:26 -0700430 if (shouldDisableSnapshots()) {
Jorim Jaggi51304d72017-05-17 17:25:32 +0200431 listener.onScreenOff();
432 return;
433 }
434
435 // We can't take a snapshot when screen is off, so take a snapshot now!
436 mHandler.post(() -> {
437 try {
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700438 synchronized (mService.mGlobalLock) {
Jorim Jaggi51304d72017-05-17 17:25:32 +0200439 mTmpTasks.clear();
440 mService.mRoot.forAllTasks(task -> {
441 if (task.isVisible()) {
442 mTmpTasks.add(task);
443 }
444 });
445 snapshotTasks(mTmpTasks);
446 }
447 } finally {
448 listener.onScreenOff();
449 }
450 });
451 }
452
Winson Chung173020c2018-05-04 15:36:47 -0700453 /**
454 * @return The SystemUI visibility flags for the top fullscreen window in the given
455 * {@param task}.
456 */
457 private int getSystemUiVisibility(Task task) {
458 final AppWindowToken topFullscreenToken = task.getTopFullscreenAppToken();
459 final WindowState topFullscreenWindow = topFullscreenToken != null
460 ? topFullscreenToken.getTopFullscreenWindow()
461 : null;
462 if (topFullscreenWindow != null) {
463 return topFullscreenWindow.getSystemUiVisibility();
464 }
465 return 0;
466 }
467
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100468 void dump(PrintWriter pw, String prefix) {
469 mCache.dump(pw, prefix);
470 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800471}