blob: 4cb5de44bef006d7741809da4070b9ab19540072 [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
Chiawei Wang02202d12019-01-03 18:12:13 +080019import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
20
Matthew Ngcb7ac672017-07-21 17:27:42 -070021import static com.android.server.wm.TaskSnapshotPersister.DISABLE_FULL_SIZED_BITMAPS;
chaviwfbe47df2017-11-10 16:14:49 -080022import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SCREENSHOT;
Winson Chung3e13ef82017-06-29 12:41:14 -070023import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
24import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
25
Evan Rosky08e20932019-05-14 10:54:07 -070026import android.annotation.NonNull;
Jorim Jaggi02886a82016-12-06 09:10:06 -080027import android.annotation.Nullable;
Jorim Jaggi3d732602017-02-22 17:56:40 +010028import android.app.ActivityManager;
Jorim Jaggie2c77f92016-12-29 14:57:22 +010029import android.app.ActivityManager.TaskSnapshot;
Keyvan Amirie681cec2017-06-05 22:48:26 -070030import android.content.pm.PackageManager;
Jorim Jaggi6aead1c2017-05-23 15:07:44 +020031import android.graphics.Bitmap;
Jorim Jaggi02886a82016-12-06 09:10:06 -080032import android.graphics.GraphicBuffer;
Winson Chung02378b42018-06-04 16:40:09 -070033import android.graphics.PixelFormat;
John Reck32f140aa62018-10-04 15:08:24 -070034import android.graphics.RecordingCanvas;
Jorim Jaggi30d64f32017-04-07 16:33:17 +020035import android.graphics.Rect;
John Reck32f140aa62018-10-04 15:08:24 -070036import android.graphics.RenderNode;
Jorim Jaggif9084ec2017-01-16 13:16:59 +010037import android.os.Environment;
Jorim Jaggi51304d72017-05-17 17:25:32 +020038import android.os.Handler;
Jorim Jaggi02886a82016-12-06 09:10:06 -080039import android.util.ArraySet;
Winson Chung3e13ef82017-06-29 12:41:14 -070040import android.util.Slog;
chaviwfbe47df2017-11-10 16:14:49 -080041import android.view.SurfaceControl;
Winson Chung91092762017-05-24 14:08:48 -070042import android.view.ThreadedRenderer;
Jorim Jaggid635a4a2017-05-03 15:21:26 +020043import android.view.WindowManager.LayoutParams;
Guang Zhu09486a32017-06-06 03:27:44 +000044
Jorim Jaggi02886a82016-12-06 09:10:06 -080045import com.android.internal.annotations.VisibleForTesting;
Winson Chung51f42d22018-02-01 14:59:38 -080046import com.android.internal.graphics.ColorUtils;
Adrian Roose99bc052017-11-20 17:55:31 +010047import com.android.server.policy.WindowManagerPolicy.ScreenOffListener;
48import com.android.server.policy.WindowManagerPolicy.StartingSurface;
Jorim Jaggid635a4a2017-05-03 15:21:26 +020049import com.android.server.wm.TaskSnapshotSurface.SystemBarBackgroundPainter;
Jorim Jaggi817ebdd2018-03-26 15:46:01 +020050import com.android.server.wm.utils.InsetUtils;
Jorim Jaggi02886a82016-12-06 09:10:06 -080051
Adrian Roose99bc052017-11-20 17:55:31 +010052import com.google.android.collect.Sets;
53
Jorim Jaggi10abe2f2017-01-03 16:44:46 +010054import java.io.PrintWriter;
55
Jorim Jaggi02886a82016-12-06 09:10:06 -080056/**
57 * When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
58 * put it into our cache. Internally we use gralloc buffers to be able to draw them wherever we
59 * like without any copying.
60 * <p>
61 * System applications may retrieve a snapshot to represent the current state of a task, and draw
62 * them in their own process.
63 * <p>
64 * When we task becomes visible again, we show a starting window with the snapshot as the content to
65 * make app transitions more responsive.
66 * <p>
67 * To access this class, acquire the global window manager lock.
68 */
69class TaskSnapshotController {
Winson Chung3e13ef82017-06-29 12:41:14 -070070 private static final String TAG = TAG_WITH_CLASS_NAME ? "TaskSnapshotController" : TAG_WM;
Jorim Jaggi02886a82016-12-06 09:10:06 -080071
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +010072 /**
73 * Return value for {@link #getSnapshotMode}: We are allowed to take a real screenshot to be
74 * used as the snapshot.
75 */
76 @VisibleForTesting
77 static final int SNAPSHOT_MODE_REAL = 0;
78
79 /**
80 * Return value for {@link #getSnapshotMode}: We are not allowed to take a real screenshot but
81 * we should try to use the app theme to create a dummy representation of the app.
82 */
83 @VisibleForTesting
84 static final int SNAPSHOT_MODE_APP_THEME = 1;
85
86 /**
87 * Return value for {@link #getSnapshotMode}: We aren't allowed to take any snapshot.
88 */
89 @VisibleForTesting
90 static final int SNAPSHOT_MODE_NONE = 2;
91
Mark Renouf98ba8e62019-11-18 12:42:16 -050092 /**
93 * Constant for <code>scaleFactor</code> when calling {@link #snapshotTask} which is
94 * interpreted as using the most appropriate scale ratio for the system.
95 * This may yield a smaller ratio on low memory devices.
96 */
97 @VisibleForTesting
98 static final float SNAPSHOT_SCALE_AUTO = -1f;
99
Jorim Jaggi02886a82016-12-06 09:10:06 -0800100 private final WindowManagerService mService;
Jorim Jaggi02886a82016-12-06 09:10:06 -0800101
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100102 private final TaskSnapshotCache mCache;
Kevin716a9db2018-12-18 16:53:39 -0800103 private final TaskSnapshotPersister mPersister;
104 private final TaskSnapshotLoader mLoader;
Winson Chung23aa7b12018-02-01 11:41:43 -0800105 private final ArraySet<Task> mSkipClosingAppSnapshotTasks = new ArraySet<>();
Jorim Jaggi02886a82016-12-06 09:10:06 -0800106 private final ArraySet<Task> mTmpTasks = new ArraySet<>();
Jorim Jaggi51304d72017-05-17 17:25:32 +0200107 private final Handler mHandler = new Handler();
Winson Chungffde2ea2019-06-17 17:19:13 -0700108 private final float mFullSnapshotScale;
Jorim Jaggi02886a82016-12-06 09:10:06 -0800109
chaviw23ee71c2017-12-18 11:29:41 -0800110 private final Rect mTmpRect = new Rect();
111
Keyvan Amirie681cec2017-06-05 22:48:26 -0700112 /**
113 * Flag indicating whether we are running on an Android TV device.
114 */
115 private final boolean mIsRunningOnTv;
116
Juho Haf5dd7cc2017-06-15 11:38:56 +0900117 /**
118 * Flag indicating whether we are running on an IoT device.
119 */
120 private final boolean mIsRunningOnIoT;
121
Matthew Ng95378192017-08-16 11:57:00 -0700122 /**
123 * Flag indicating whether we are running on an Android Wear device.
124 */
125 private final boolean mIsRunningOnWear;
126
Jorim Jaggi02886a82016-12-06 09:10:06 -0800127 TaskSnapshotController(WindowManagerService service) {
128 mService = service;
Kevin716a9db2018-12-18 16:53:39 -0800129 mPersister = new TaskSnapshotPersister(mService, Environment::getDataSystemCeDirectory);
130 mLoader = new TaskSnapshotLoader(mPersister);
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100131 mCache = new TaskSnapshotCache(mService, mLoader);
Keyvan Amirie681cec2017-06-05 22:48:26 -0700132 mIsRunningOnTv = mService.mContext.getPackageManager().hasSystemFeature(
133 PackageManager.FEATURE_LEANBACK);
Juho Haf5dd7cc2017-06-15 11:38:56 +0900134 mIsRunningOnIoT = mService.mContext.getPackageManager().hasSystemFeature(
135 PackageManager.FEATURE_EMBEDDED);
Matthew Ng95378192017-08-16 11:57:00 -0700136 mIsRunningOnWear = mService.mContext.getPackageManager().hasSystemFeature(
137 PackageManager.FEATURE_WATCH);
Winson Chungffde2ea2019-06-17 17:19:13 -0700138 mFullSnapshotScale = mService.mContext.getResources().getFloat(
139 com.android.internal.R.dimen.config_fullTaskSnapshotScale);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800140 }
141
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100142 void systemReady() {
143 mPersister.start();
144 }
145
lumark588a3e82018-07-20 18:53:54 +0800146 void onTransitionStarting(DisplayContent displayContent) {
147 handleClosingApps(displayContent.mClosingApps);
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100148 }
149
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100150 /**
151 * Called when the visibility of an app changes outside of the regular app transition flow.
152 */
Garfield Tane8d84ab2019-10-11 09:49:40 -0700153 void notifyAppVisibilityChanged(ActivityRecord appWindowToken, boolean visible) {
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100154 if (!visible) {
155 handleClosingApps(Sets.newArraySet(appWindowToken));
156 }
157 }
158
Garfield Tane8d84ab2019-10-11 09:49:40 -0700159 private void handleClosingApps(ArraySet<ActivityRecord> closingApps) {
Keyvan Amirie681cec2017-06-05 22:48:26 -0700160 if (shouldDisableSnapshots()) {
Jorim Jaggi3d732602017-02-22 17:56:40 +0100161 return;
162 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800163
164 // We need to take a snapshot of the task if and only if all activities of the task are
165 // either closing or hidden.
Jorim Jaggi8b702ed2017-01-20 16:59:03 +0100166 getClosingTasks(closingApps, mTmpTasks);
Jorim Jaggi51304d72017-05-17 17:25:32 +0200167 snapshotTasks(mTmpTasks);
Winson Chung23aa7b12018-02-01 11:41:43 -0800168 mSkipClosingAppSnapshotTasks.clear();
Jorim Jaggi51304d72017-05-17 17:25:32 +0200169 }
170
Winson Chung23aa7b12018-02-01 11:41:43 -0800171 /**
172 * Adds the given {@param tasks} to the list of tasks which should not have their snapshots
173 * taken upon the next processing of the set of closing apps. The caller is responsible for
174 * calling {@link #snapshotTasks} to ensure that the task has an up-to-date snapshot.
175 */
176 @VisibleForTesting
177 void addSkipClosingAppSnapshotTasks(ArraySet<Task> tasks) {
178 mSkipClosingAppSnapshotTasks.addAll(tasks);
179 }
180
181 void snapshotTasks(ArraySet<Task> tasks) {
Winson Chungbed6c322019-12-20 16:23:10 -0800182 snapshotTasks(tasks, false /* allowSnapshotHome */);
Riddle Hsu440f88b2019-11-06 22:17:35 +0800183 }
184
185 private void snapshotTasks(ArraySet<Task> tasks, boolean allowSnapshotHome) {
Jorim Jaggi51304d72017-05-17 17:25:32 +0200186 for (int i = tasks.size() - 1; i >= 0; i--) {
187 final Task task = tasks.valueAt(i);
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100188 final TaskSnapshot snapshot;
Riddle Hsu440f88b2019-11-06 22:17:35 +0800189 final boolean snapshotHome = allowSnapshotHome && task.isActivityTypeHome();
190 if (snapshotHome) {
191 snapshot = snapshotTask(task);
192 } else {
193 switch (getSnapshotMode(task)) {
194 case SNAPSHOT_MODE_NONE:
195 continue;
196 case SNAPSHOT_MODE_APP_THEME:
197 snapshot = drawAppThemeSnapshot(task);
198 break;
199 case SNAPSHOT_MODE_REAL:
200 snapshot = snapshotTask(task);
201 break;
202 default:
203 snapshot = null;
204 break;
205 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800206 }
Jorim Jaggie2c77f92016-12-29 14:57:22 +0100207 if (snapshot != null) {
Winson Chung3e13ef82017-06-29 12:41:14 -0700208 final GraphicBuffer buffer = snapshot.getSnapshot();
209 if (buffer.getWidth() == 0 || buffer.getHeight() == 0) {
210 buffer.destroy();
211 Slog.e(TAG, "Invalid task snapshot dimensions " + buffer.getWidth() + "x"
212 + buffer.getHeight());
213 } else {
214 mCache.putSnapshot(task, snapshot);
Riddle Hsu440f88b2019-11-06 22:17:35 +0800215 // Don't persist or notify the change for the temporal snapshot.
216 if (!snapshotHome) {
217 mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
218 task.onSnapshotChanged(snapshot);
219 }
Jorim Jaggifb9d78a2017-01-05 18:57:12 +0100220 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800221 }
222 }
223 }
224
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100225 /**
226 * Retrieves a snapshot. If {@param restoreFromDisk} equals {@code true}, DO HOLD THE WINDOW
227 * MANAGER LOCK WHEN CALLING THIS METHOD!
228 */
Jorim Jaggi35e3f532017-03-17 17:06:50 +0100229 @Nullable TaskSnapshot getSnapshot(int taskId, int userId, boolean restoreFromDisk,
230 boolean reducedResolution) {
Matthew Ngcb7ac672017-07-21 17:27:42 -0700231 return mCache.getSnapshot(taskId, userId, restoreFromDisk, reducedResolution
232 || DISABLE_FULL_SIZED_BITMAPS);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800233 }
234
235 /**
Jay Aliomer8b2671b2019-10-24 13:18:06 -0400236 * @see WindowManagerInternal#clearSnapshotCache
237 */
238 public void clearSnapshotCache() {
239 mCache.clearRunningCache();
240 }
241
242 /**
Jorim Jaggi02886a82016-12-06 09:10:06 -0800243 * Creates a starting surface for {@param token} with {@param snapshot}. DO NOT HOLD THE WINDOW
244 * MANAGER LOCK WHEN CALLING THIS METHOD!
245 */
Garfield Tane8d84ab2019-10-11 09:49:40 -0700246 StartingSurface createStartingSurface(ActivityRecord activity,
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200247 TaskSnapshot snapshot) {
Garfield Tane8d84ab2019-10-11 09:49:40 -0700248 return TaskSnapshotSurface.create(mService, activity, snapshot);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800249 }
250
Issei Suzuki88e21422018-07-24 16:29:09 +0200251 /**
252 * Find the window for a given task to take a snapshot. Top child of the task is usually the one
253 * we're looking for, but during app transitions, trampoline activities can appear in the
254 * children, which should be ignored.
255 */
Garfield Tane8d84ab2019-10-11 09:49:40 -0700256 @Nullable private ActivityRecord findAppTokenForSnapshot(Task task) {
Wale Ogunwalea38654f2019-11-17 20:37:15 -0800257 return task.getActivity((r) -> {
258 if (r == null || !r.isSurfaceShowing() || r.findMainWindow() == null) {
259 return false;
Issei Suzuki88e21422018-07-24 16:29:09 +0200260 }
Wale Ogunwalea38654f2019-11-17 20:37:15 -0800261 return r.forAllWindows(
Issei Suzuki88e21422018-07-24 16:29:09 +0200262 // Ensure at least one window for the top app is visible before attempting to
263 // take a screenshot. Visible here means that the WSA surface is shown and has
264 // an alpha greater than 0.
265 ws -> ws.mWinAnimator != null && ws.mWinAnimator.getShown()
266 && ws.mWinAnimator.mLastAlpha > 0f, true /* traverseTopToBottom */);
Wale Ogunwalea38654f2019-11-17 20:37:15 -0800267
268 });
Issei Suzuki88e21422018-07-24 16:29:09 +0200269 }
270
Mark Renouf98ba8e62019-11-18 12:42:16 -0500271 /**
272 * Validates the state of the Task is appropriate to capture a snapshot, collects
273 * information from the task and populates the builder.
274 *
275 * @param task the task to capture
276 * @param scaleFraction the scale fraction between 0-1.0, or {@link #SNAPSHOT_SCALE_AUTO}
277 * to automatically select
278 * @param pixelFormat the desired pixel format, or {@link PixelFormat#UNKNOWN} to
279 * automatically select
280 * @param builder the snapshot builder to populate
281 *
282 * @return true if the state of the task is ok to proceed
283 */
Riddle Hsuc802e4b2019-12-20 16:54:33 +0800284 @VisibleForTesting
285 boolean prepareTaskSnapshot(Task task, float scaleFraction, int pixelFormat,
Mark Renouf98ba8e62019-11-18 12:42:16 -0500286 TaskSnapshot.Builder builder) {
287 if (!mService.mPolicy.isScreenOn()) {
288 if (DEBUG_SCREENSHOT) {
289 Slog.i(TAG_WM, "Attempted to take screenshot while display was off.");
290 }
291 return false;
292 }
293 final ActivityRecord activity = findAppTokenForSnapshot(task);
294 if (activity == null) {
295 if (DEBUG_SCREENSHOT) {
296 Slog.w(TAG_WM, "Failed to take screenshot. No visible windows for " + task);
297 }
298 return false;
299 }
300 if (activity.hasCommittedReparentToAnimationLeash()) {
301 if (DEBUG_SCREENSHOT) {
302 Slog.w(TAG_WM, "Failed to take screenshot. App is animating " + activity);
303 }
304 return false;
305 }
306
307 final WindowState mainWindow = activity.findMainWindow();
308 if (mainWindow == null) {
309 Slog.w(TAG_WM, "Failed to take screenshot. No main window for " + task);
310 return false;
311 }
312
313 builder.setIsRealSnapshot(true);
314 builder.setId(System.currentTimeMillis());
315 builder.setContentInsets(getInsets(mainWindow));
316
317 final boolean isLowRamDevice = ActivityManager.isLowRamDeviceStatic();
318
319 if (scaleFraction == SNAPSHOT_SCALE_AUTO) {
320 builder.setScaleFraction(isLowRamDevice
321 ? mPersister.getReducedScale()
322 : mFullSnapshotScale);
323 builder.setReducedResolution(isLowRamDevice);
324 } else {
325 builder.setScaleFraction(scaleFraction);
326 builder.setReducedResolution(scaleFraction < 1.0f);
327 }
328
329 final boolean isWindowTranslucent = mainWindow.getAttrs().format != PixelFormat.OPAQUE;
330 final boolean isShowWallpaper = (mainWindow.getAttrs().flags & FLAG_SHOW_WALLPAPER) != 0;
331
332 if (pixelFormat == PixelFormat.UNKNOWN) {
333 pixelFormat = mPersister.use16BitFormat() && activity.fillsParent()
334 && !(isWindowTranslucent && isShowWallpaper)
335 ? PixelFormat.RGB_565
336 : PixelFormat.RGBA_8888;
337 }
338
339 final boolean isTranslucent = PixelFormat.formatHasAlpha(pixelFormat)
340 && (!activity.fillsParent() || isWindowTranslucent);
341
342 builder.setTopActivityComponent(activity.mActivityComponent);
Riddle Hsuc802e4b2019-12-20 16:54:33 +0800343 builder.setPixelFormat(pixelFormat);
Mark Renouf98ba8e62019-11-18 12:42:16 -0500344 builder.setIsTranslucent(isTranslucent);
345 builder.setOrientation(activity.getTask().getConfiguration().orientation);
346 builder.setWindowingMode(task.getWindowingMode());
347 builder.setSystemUiVisibility(getSystemUiVisibility(task));
348 return true;
349 }
350
Evan Rosky08e20932019-05-14 10:54:07 -0700351 @Nullable
352 SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
353 float scaleFraction) {
Chiawei Wang02202d12019-01-03 18:12:13 +0800354 return createTaskSnapshot(task, scaleFraction, PixelFormat.RGBA_8888);
355 }
356
357 @Nullable
358 SurfaceControl.ScreenshotGraphicBuffer createTaskSnapshot(@NonNull Task task,
359 float scaleFraction, int pixelFormat) {
Evan Rosky08e20932019-05-14 10:54:07 -0700360 if (task.getSurfaceControl() == null) {
361 if (DEBUG_SCREENSHOT) {
362 Slog.w(TAG_WM, "Failed to take screenshot. No surface control for " + task);
363 }
364 return null;
365 }
366 task.getBounds(mTmpRect);
367 mTmpRect.offsetTo(0, 0);
368 final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
369 SurfaceControl.captureLayers(
Chiawei Wang02202d12019-01-03 18:12:13 +0800370 task.getSurfaceControl(), mTmpRect, scaleFraction, pixelFormat);
Evan Rosky08e20932019-05-14 10:54:07 -0700371 final GraphicBuffer buffer = screenshotBuffer != null ? screenshotBuffer.getGraphicBuffer()
372 : null;
373 if (buffer == null || buffer.getWidth() <= 1 || buffer.getHeight() <= 1) {
374 return null;
375 }
376 return screenshotBuffer;
377 }
378
Mark Renouf98ba8e62019-11-18 12:42:16 -0500379 @Nullable
380 TaskSnapshot snapshotTask(Task task) {
381 return snapshotTask(task, SNAPSHOT_SCALE_AUTO, PixelFormat.UNKNOWN);
382 }
383
384 @Nullable
385 TaskSnapshot snapshotTask(Task task, float scaleFraction, int pixelFormat) {
386 TaskSnapshot.Builder builder = new TaskSnapshot.Builder();
387
388 if (!prepareTaskSnapshot(task, scaleFraction, pixelFormat, builder)) {
389 // Failed some pre-req. Has been logged.
chaviwfbe47df2017-11-10 16:14:49 -0800390 return null;
391 }
chaviw7f1fa992018-01-10 13:52:12 -0800392
Peiyong Line3e5efd2019-03-21 20:59:47 +0000393 final SurfaceControl.ScreenshotGraphicBuffer screenshotBuffer =
Mark Renouf98ba8e62019-11-18 12:42:16 -0500394 createTaskSnapshot(task, builder.getScaleFraction(),
395 builder.getPixelFormat());
Evan Rosky08e20932019-05-14 10:54:07 -0700396
397 if (screenshotBuffer == null) {
Mark Renouf98ba8e62019-11-18 12:42:16 -0500398 // Failed to acquire image. Has been logged.
Jorim Jaggi02886a82016-12-06 09:10:06 -0800399 return null;
400 }
Mark Renouf98ba8e62019-11-18 12:42:16 -0500401 builder.setSnapshot(screenshotBuffer.getGraphicBuffer());
402 builder.setColorSpace(screenshotBuffer.getColorSpace());
403 return builder.build();
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200404 }
405
Keyvan Amirie681cec2017-06-05 22:48:26 -0700406 private boolean shouldDisableSnapshots() {
Jorim Jaggie7d2b852017-08-28 17:55:15 +0200407 return mIsRunningOnWear || mIsRunningOnTv || mIsRunningOnIoT;
Keyvan Amirie681cec2017-06-05 22:48:26 -0700408 }
409
Jorim Jaggi817ebdd2018-03-26 15:46:01 +0200410 private Rect getInsets(WindowState state) {
Adrian Roos20e07892018-02-23 19:12:01 +0100411 // XXX(b/72757033): These are insets relative to the window frame, but we're really
412 // interested in the insets relative to the task bounds.
chaviw9c81e632018-07-31 11:17:52 -0700413 final Rect insets = minRect(state.getContentInsets(), state.getStableInsets());
Garfield Tane8d84ab2019-10-11 09:49:40 -0700414 InsetUtils.addInsets(insets, state.mActivityRecord.getLetterboxInsets());
Adrian Roos20e07892018-02-23 19:12:01 +0100415 return insets;
416 }
Adrian Roos98a146d2017-11-29 16:39:44 +0100417
Adrian Roos20e07892018-02-23 19:12:01 +0100418 private Rect minRect(Rect rect1, Rect rect2) {
419 return new Rect(Math.min(rect1.left, rect2.left),
420 Math.min(rect1.top, rect2.top),
421 Math.min(rect1.right, rect2.right),
422 Math.min(rect1.bottom, rect2.bottom));
423 }
Adrian Roos98a146d2017-11-29 16:39:44 +0100424
Jorim Jaggi02886a82016-12-06 09:10:06 -0800425 /**
426 * Retrieves all closing tasks based on the list of closing apps during an app transition.
427 */
428 @VisibleForTesting
Garfield Tane8d84ab2019-10-11 09:49:40 -0700429 void getClosingTasks(ArraySet<ActivityRecord> closingApps, ArraySet<Task> outClosingTasks) {
Jorim Jaggi02886a82016-12-06 09:10:06 -0800430 outClosingTasks.clear();
431 for (int i = closingApps.size() - 1; i >= 0; i--) {
Garfield Tane8d84ab2019-10-11 09:49:40 -0700432 final ActivityRecord activity = closingApps.valueAt(i);
433 final Task task = activity.getTask();
Jorim Jaggi02886a82016-12-06 09:10:06 -0800434
435 // If the task of the app is not visible anymore, it means no other app in that task
436 // is opening. Thus, the task is closing.
Winson Chung23aa7b12018-02-01 11:41:43 -0800437 if (task != null && !task.isVisible() && !mSkipClosingAppSnapshotTasks.contains(task)) {
Bryce Lee6d410262017-02-28 15:30:17 -0800438 outClosingTasks.add(task);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800439 }
440 }
441 }
442
Jorim Jaggi0fe7ce962017-02-22 16:45:48 +0100443 @VisibleForTesting
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100444 int getSnapshotMode(Task task) {
Wale Ogunwalea38654f2019-11-17 20:37:15 -0800445 final ActivityRecord topChild = task.getTopMostActivity();
Winson Chungbff18372018-04-27 16:12:54 -0700446 if (!task.isActivityTypeStandardOrUndefined() && !task.isActivityTypeAssistant()) {
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100447 return SNAPSHOT_MODE_NONE;
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200448 } else if (topChild != null && topChild.shouldUseAppThemeSnapshot()) {
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100449 return SNAPSHOT_MODE_APP_THEME;
450 } else {
451 return SNAPSHOT_MODE_REAL;
452 }
453 }
454
455 /**
456 * If we are not allowed to take a real screenshot, this attempts to represent the app as best
457 * as possible by using the theme's window background.
458 */
459 private TaskSnapshot drawAppThemeSnapshot(Task task) {
Wale Ogunwalea38654f2019-11-17 20:37:15 -0800460 final ActivityRecord topChild = task.getTopMostActivity();
Jorim Jaggi8f4fe6e2017-03-14 18:21:40 +0100461 if (topChild == null) {
462 return null;
463 }
464 final WindowState mainWindow = topChild.findMainWindow();
465 if (mainWindow == null) {
466 return null;
467 }
Winson Chung51f42d22018-02-01 14:59:38 -0800468 final int color = ColorUtils.setAlphaComponent(
469 task.getTaskDescription().getBackgroundColor(), 255);
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200470 final LayoutParams attrs = mainWindow.getAttrs();
471 final SystemBarBackgroundPainter decorPainter = new SystemBarBackgroundPainter(attrs.flags,
Winson Chungffde2ea2019-06-17 17:19:13 -0700472 attrs.privateFlags, attrs.systemUiVisibility, task.getTaskDescription(),
Jorim Jaggi0dd0cf92019-12-27 15:17:44 +0100473 mFullSnapshotScale, mainWindow.getRequestedInsetsState());
Winson Chungffde2ea2019-06-17 17:19:13 -0700474 final int width = (int) (task.getBounds().width() * mFullSnapshotScale);
475 final int height = (int) (task.getBounds().height() * mFullSnapshotScale);
Winson Chung91092762017-05-24 14:08:48 -0700476
477 final RenderNode node = RenderNode.create("TaskSnapshotController", null);
478 node.setLeftTopRightBottom(0, 0, width, height);
479 node.setClipToBounds(false);
John Reck32f140aa62018-10-04 15:08:24 -0700480 final RecordingCanvas c = node.start(width, height);
Winson Chung91092762017-05-24 14:08:48 -0700481 c.drawColor(color);
chaviw9c81e632018-07-31 11:17:52 -0700482 decorPainter.setInsets(mainWindow.getContentInsets(), mainWindow.getStableInsets());
Jorim Jaggid635a4a2017-05-03 15:21:26 +0200483 decorPainter.drawDecors(c, null /* statusBarExcludeFrame */);
Winson Chung91092762017-05-24 14:08:48 -0700484 node.end(c);
485 final Bitmap hwBitmap = ThreadedRenderer.createHardwareBitmap(node, width, height);
Winson Chung6267f992017-10-06 14:01:10 -0700486 if (hwBitmap == null) {
487 return null;
488 }
Winson Chung67d93c22019-06-13 10:36:58 -0700489
Winson Chung02378b42018-06-04 16:40:09 -0700490 // Note, the app theme snapshot is never translucent because we enforce a non-translucent
491 // color above
Hongwei Wang3b9bdcf2019-07-15 10:23:03 -0700492 return new TaskSnapshot(
493 System.currentTimeMillis() /* id */,
494 topChild.mActivityComponent, hwBitmap.createGraphicBufferHandle(),
Winson Chung67d93c22019-06-13 10:36:58 -0700495 hwBitmap.getColorSpace(), topChild.getTask().getConfiguration().orientation,
Jorim Jaggi2be23a92019-06-13 16:22:35 +0200496 getInsets(mainWindow), ActivityManager.isLowRamDeviceStatic() /* reduced */,
Winson Chungffde2ea2019-06-17 17:19:13 -0700497 mFullSnapshotScale, false /* isRealSnapshot */, task.getWindowingMode(),
Peiyong Lin21e499a2019-04-03 16:37:46 -0700498 getSystemUiVisibility(task), false);
Jorim Jaggi02886a82016-12-06 09:10:06 -0800499 }
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100500
501 /**
Garfield Tane8d84ab2019-10-11 09:49:40 -0700502 * Called when an {@link ActivityRecord} has been removed.
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100503 */
Garfield Tane8d84ab2019-10-11 09:49:40 -0700504 void onAppRemoved(ActivityRecord activity) {
505 mCache.onAppRemoved(activity);
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100506 }
507
508 /**
Garfield Tane8d84ab2019-10-11 09:49:40 -0700509 * Called when the process of an {@link ActivityRecord} has died.
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100510 */
Garfield Tane8d84ab2019-10-11 09:49:40 -0700511 void onAppDied(ActivityRecord activity) {
512 mCache.onAppDied(activity);
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100513 }
514
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100515 void notifyTaskRemovedFromRecents(int taskId, int userId) {
Jorim Jaggi7361bab2017-01-16 17:17:58 +0100516 mCache.onTaskRemoved(taskId);
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100517 mPersister.onTaskRemovedFromRecents(taskId, userId);
518 }
519
Riddle Hsu440f88b2019-11-06 22:17:35 +0800520 void removeSnapshotCache(int taskId) {
521 mCache.removeRunningEntry(taskId);
522 }
523
Jorim Jaggif9084ec2017-01-16 13:16:59 +0100524 /**
525 * See {@link TaskSnapshotPersister#removeObsoleteFiles}
526 */
527 void removeObsoleteTaskFiles(ArraySet<Integer> persistentTaskIds, int[] runningUserIds) {
528 mPersister.removeObsoleteFiles(persistentTaskIds, runningUserIds);
529 }
530
Jorim Jaggia41b7292017-05-11 23:50:34 +0200531 /**
532 * Temporarily pauses/unpauses persisting of task snapshots.
533 *
534 * @param paused Whether task snapshot persisting should be paused.
535 */
536 void setPersisterPaused(boolean paused) {
537 mPersister.setPaused(paused);
538 }
539
Jorim Jaggi51304d72017-05-17 17:25:32 +0200540 /**
541 * Called when screen is being turned off.
542 */
543 void screenTurningOff(ScreenOffListener listener) {
Keyvan Amirie681cec2017-06-05 22:48:26 -0700544 if (shouldDisableSnapshots()) {
Jorim Jaggi51304d72017-05-17 17:25:32 +0200545 listener.onScreenOff();
546 return;
547 }
548
549 // We can't take a snapshot when screen is off, so take a snapshot now!
550 mHandler.post(() -> {
551 try {
Wale Ogunwaledb485de2018-10-29 09:47:07 -0700552 synchronized (mService.mGlobalLock) {
Jorim Jaggi51304d72017-05-17 17:25:32 +0200553 mTmpTasks.clear();
554 mService.mRoot.forAllTasks(task -> {
555 if (task.isVisible()) {
556 mTmpTasks.add(task);
557 }
558 });
Riddle Hsu440f88b2019-11-06 22:17:35 +0800559 // Allow taking snapshot of home when turning screen off to reduce the delay of
560 // unlocking/waking to home.
561 snapshotTasks(mTmpTasks, true /* allowSnapshotHome */);
Jorim Jaggi51304d72017-05-17 17:25:32 +0200562 }
563 } finally {
564 listener.onScreenOff();
565 }
566 });
567 }
568
Winson Chung173020c2018-05-04 15:36:47 -0700569 /**
Riddle Hsu84bc3d72019-12-19 19:24:07 +0800570 * @return The SystemUI visibility flags for the top fullscreen opaque window in the given
Winson Chung173020c2018-05-04 15:36:47 -0700571 * {@param task}.
572 */
573 private int getSystemUiVisibility(Task task) {
Garfield Tane8d84ab2019-10-11 09:49:40 -0700574 final ActivityRecord topFullscreenActivity = task.getTopFullscreenActivity();
Riddle Hsu84bc3d72019-12-19 19:24:07 +0800575 final WindowState topFullscreenOpaqueWindow = topFullscreenActivity != null
576 ? topFullscreenActivity.getTopFullscreenOpaqueWindow()
Winson Chung173020c2018-05-04 15:36:47 -0700577 : null;
Riddle Hsu84bc3d72019-12-19 19:24:07 +0800578 if (topFullscreenOpaqueWindow != null) {
579 return topFullscreenOpaqueWindow.getSystemUiVisibility();
Winson Chung173020c2018-05-04 15:36:47 -0700580 }
581 return 0;
582 }
583
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100584 void dump(PrintWriter pw, String prefix) {
Winson Chungffde2ea2019-06-17 17:19:13 -0700585 pw.println(prefix + "mFullSnapshotScale=" + mFullSnapshotScale);
Jorim Jaggi10abe2f2017-01-03 16:44:46 +0100586 mCache.dump(pw, prefix);
587 }
Jorim Jaggi02886a82016-12-06 09:10:06 -0800588}