Initial implementation of snapshots

All this functionality is hidden behind a flag. If this flag is
active, we disable the regular screenshots.

Instead, we take a screenshot when an app transition for which a
task is disappearing is starting. The screenshot gets stored
into a gralloc buffer. SystemUI uses a new method to retrieve
a snapshot gralloc buffer and then draws it using GraphicBuffer.
createHardwareBitmap().

When starting an existing activity in an existing tasks, or when
bringing an existing tasks to front from recents, we add a new
snapshot starting window. For that, we reuse the existing
starting window, but when creating the window, we use a fake
window that draws the contents of the starting window.

Test: runtest frameworks-services -c
com.android.server.wm.TaskSnapshotControllerTest
Bug: 31339431
Change-Id: If72df07b3e56f30413db5029d0887b8c9665aaf4
diff --git a/core/java/android/app/ActivityManager.java b/core/java/android/app/ActivityManager.java
index 9037304..2f8089d 100644
--- a/core/java/android/app/ActivityManager.java
+++ b/core/java/android/app/ActivityManager.java
@@ -125,6 +125,16 @@
 
     private static volatile boolean sSystemReady = false;
 
+    /**
+     * System property to enable task snapshots.
+     * @hide
+     */
+    public final static boolean ENABLE_TASK_SNAPSHOTS;
+
+    static {
+        ENABLE_TASK_SNAPSHOTS = SystemProperties.getBoolean("persist.enable_task_snapshots", false);
+    }
+
     static final class UidObserver extends IUidObserver.Stub {
         final OnUidImportanceListener mListener;
 
diff --git a/core/java/android/app/IActivityManager.aidl b/core/java/android/app/IActivityManager.aidl
index 2ed9eab..a4c80b8 100644
--- a/core/java/android/app/IActivityManager.aidl
+++ b/core/java/android/app/IActivityManager.aidl
@@ -53,6 +53,7 @@
 import android.content.pm.UserInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.net.Uri;
@@ -596,6 +597,11 @@
     /** Cancels the thumbnail transitions for the given task. */
     void cancelTaskThumbnailTransition(int taskId);
 
+    /**
+     * @return a graphic buffer representing a screenshot of a task
+     */
+    GraphicBuffer getTaskSnapshot(int taskId);
+
     // WARNING: when these transactions are updated, check if they are any callers on the native
     // side. If so, make sure they are using the correct transaction ids and arguments.
     // If a transaction which will also be used on the native side is being inserted, add it
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
index aa7631d..aac3c18 100644
--- a/core/java/android/view/WindowManager.java
+++ b/core/java/android/view/WindowManager.java
@@ -1319,6 +1319,17 @@
         public static final int PRIVATE_FLAG_SUSTAINED_PERFORMANCE_MODE = 0x00040000;
 
         /**
+         * Flag to indicate that this window is used as a task snapshot window. A task snapshot
+         * window is a starting window that gets shown with a screenshot from the previous state
+         * that is active until the app has drawn its first frame.
+         *
+         * <p>If this flag is set, SystemUI flags are ignored such that the real window behind can
+         * set the SystemUI flags.
+         * @hide
+         */
+        public static final int PRIVATE_FLAG_TASK_SNAPSHOT = 0x00080000;
+
+        /**
          * Control flags that are private to the platform.
          * @hide
          */
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
index dd85256..3748134 100644
--- a/core/java/android/view/WindowManagerPolicy.java
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -439,10 +439,15 @@
     /**
      * Holds the contents of a starting window. {@link #addSplashScreen} needs to wrap the
      * contents of the starting window into an class implementing this interface, which then will be
-     * held by WM and passed into {@link #removeSplashScreen} when the starting window is no
-     * longer needed.
+     * held by WM and released with {@link #remove} when no longer needed.
      */
     interface StartingSurface {
+
+        /**
+         * Removes the starting window surface. Do not hold the window manager lock when calling
+         * this method!
+         */
+        void remove();
     }
 
     /**
@@ -746,35 +751,14 @@
      * @param overrideConfig override configuration to consider when generating
      *        context to for resources.
      *
-     * @return Optionally you can return the View that was used to create the
-     *         window, for easy removal in removeSplashScreen.
+     * @return The starting surface.
      *
-     * @see #removeSplashScreen
      */
     public StartingSurface addSplashScreen(IBinder appToken, String packageName, int theme,
             CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel, int labelRes, int icon,
             int logo, int windowFlags, Configuration overrideConfig);
 
     /**
-     * Called when the first window of an application has been displayed, while
-     * {@link #addSplashScreen} has created a temporary initial window for
-     * that application.  You should at this point remove the window from the
-     * window manager.  This is called without the window manager locked so
-     * that you can call back into it.
-     *
-     * <p>Note: due to the nature of these functions not being called with the
-     * window manager locked, you must be prepared for this function to be
-     * called multiple times and/or an initial time with a null View window
-     * even if you previously returned one.
-     *
-     * @param appToken Token of the application that has started.
-     * @param surface Surface that was returned by {@link #addSplashScreen}.
-     *
-     * @see #addSplashScreen
-     */
-    public void removeSplashScreen(IBinder appToken, StartingSurface surface);
-
-    /**
      * Prepare for a window being added to the window manager.  You can throw an
      * exception here to prevent the window being added, or do whatever setup
      * you need to keep track of the window.
diff --git a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
index 7791132..08970e4 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/misc/SystemServicesProxy.java
@@ -22,13 +22,15 @@
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
+import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
 import static android.provider.Settings.Global.DEVELOPMENT_ENABLE_FREEFORM_WINDOWS_SUPPORT;
 
 import android.app.ActivityManager;
+import android.app.ActivityManager.TaskThumbnailInfo;
 import android.app.ActivityOptions;
 import android.app.AppGlobals;
 import android.app.IActivityManager;
-import android.app.ITaskStackListener;
+import android.app.KeyguardManager;
 import android.app.UiModeManager;
 import android.content.ComponentName;
 import android.content.ContentResolver;
@@ -45,6 +47,7 @@
 import android.graphics.BitmapFactory;
 import android.graphics.Canvas;
 import android.graphics.Color;
+import android.graphics.GraphicBuffer;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.PorterDuff;
@@ -74,13 +77,13 @@
 import android.view.WindowManager.KeyboardShortcutsReceiver;
 import android.view.WindowManagerGlobal;
 import android.view.accessibility.AccessibilityManager;
-import android.app.KeyguardManager;
 
 import com.android.internal.app.AssistUtils;
 import com.android.internal.os.BackgroundThread;
 import com.android.systemui.R;
 import com.android.systemui.pip.tv.PipMenuActivity;
 import com.android.systemui.pip.tv.PipOnboardingActivity;
+import com.android.systemui.recents.Constants;
 import com.android.systemui.recents.Recents;
 import com.android.systemui.recents.RecentsDebugFlags;
 import com.android.systemui.recents.RecentsImpl;
@@ -603,7 +606,7 @@
         }
 
         getThumbnail(taskId, thumbnailData);
-        if (thumbnailData.thumbnail != null) {
+        if (thumbnailData.thumbnail != null && !ActivityManager.ENABLE_TASK_SNAPSHOTS) {
             thumbnailData.thumbnail.setHasAlpha(false);
             // We use a dumb heuristic for now, if the thumbnail is purely transparent in the top
             // left pixel, then assume the whole thumbnail is transparent. Generally, proper
@@ -627,25 +630,43 @@
             return;
         }
 
-        ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
-        if (taskThumbnail == null) {
-            return;
-        }
-
-        Bitmap thumbnail = taskThumbnail.mainThumbnail;
-        ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
-        if (thumbnail == null && descriptor != null) {
-            thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
-                    null, sBitmapOptions);
-        }
-        if (descriptor != null) {
+        if (ActivityManager.ENABLE_TASK_SNAPSHOTS) {
+            GraphicBuffer graphicBuffer = null;
             try {
-                descriptor.close();
-            } catch (IOException e) {
+                graphicBuffer = ActivityManager.getService().getTaskSnapshot(taskId);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Failed to retrieve snapshot", e);
             }
+            if (graphicBuffer != null) {
+                thumbnailDataOut.thumbnail = Bitmap.createHardwareBitmap(graphicBuffer);
+            } else {
+                thumbnailDataOut.thumbnail = null;
+            }
+
+            // TODO: Retrieve screen orientation.
+            thumbnailDataOut.thumbnailInfo = new TaskThumbnailInfo();
+            thumbnailDataOut.thumbnailInfo.screenOrientation = ORIENTATION_PORTRAIT;
+        } else {
+            ActivityManager.TaskThumbnail taskThumbnail = mAm.getTaskThumbnail(taskId);
+            if (taskThumbnail == null) {
+                return;
+            }
+
+            Bitmap thumbnail = taskThumbnail.mainThumbnail;
+            ParcelFileDescriptor descriptor = taskThumbnail.thumbnailFileDescriptor;
+            if (thumbnail == null && descriptor != null) {
+                thumbnail = BitmapFactory.decodeFileDescriptor(descriptor.getFileDescriptor(),
+                        null, sBitmapOptions);
+            }
+            if (descriptor != null) {
+                try {
+                    descriptor.close();
+                } catch (IOException e) {
+                }
+            }
+            thumbnailDataOut.thumbnail = thumbnail;
+            thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo;
         }
-        thumbnailDataOut.thumbnail = thumbnail;
-        thumbnailDataOut.thumbnailInfo = taskThumbnail.thumbnailInfo;
     }
 
     /**
diff --git a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
index 58b929a..1a81446 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/views/TaskViewThumbnail.java
@@ -239,8 +239,7 @@
             // We consider this a stack task if it is not freeform (ie. has no bounds) or has been
             // dragged into the stack from the freeform workspace
             boolean isStackTask = !mTask.isFreeformTask() || mTask.bounds == null;
-            if (mTaskViewRect.isEmpty() || mThumbnailInfo == null ||
-                    mThumbnailInfo.taskWidth == 0 || mThumbnailInfo.taskHeight == 0) {
+            if (mTaskViewRect.isEmpty() || mThumbnailInfo == null) {
                 // If we haven't measured or the thumbnail is invalid, skip the thumbnail drawing
                 // and only draw the background color
                 mThumbnailScale = 0f;
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 48a7e7c..5437ad2 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -16,258 +16,11 @@
 
 package com.android.server.am;
 
-import android.annotation.Nullable;
-import android.app.ActivityManagerInternal.PictureInPictureArguments;
-import android.app.ApplicationThreadConstants;
-import android.app.ContentProviderHolder;
-import android.app.IActivityManager;
-import android.app.RemoteAction;
-import android.app.WaitResult;
-import android.os.IDeviceIdentifiersPolicyService;
-
-import com.android.internal.policy.IKeyguardDismissCallback;
-import com.android.internal.telephony.TelephonyIntents;
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import com.android.internal.R;
-import com.android.internal.annotations.GuardedBy;
-import com.android.internal.app.AssistUtils;
-import com.android.internal.app.DumpHeapActivity;
-import com.android.internal.app.IAppOpsCallback;
-import com.android.internal.app.IAppOpsService;
-import com.android.internal.app.IVoiceInteractor;
-import com.android.internal.app.ProcessMap;
-import com.android.internal.app.SystemUserHomeActivity;
-import com.android.internal.app.procstats.ProcessStats;
-import com.android.internal.os.BackgroundThread;
-import com.android.internal.os.BatteryStatsImpl;
-import com.android.internal.os.IResultReceiver;
-import com.android.internal.os.ProcessCpuTracker;
-import com.android.internal.os.TransferPipe;
-import com.android.internal.os.Zygote;
-import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.FastPrintWriter;
-import com.android.internal.util.FastXmlSerializer;
-import com.android.internal.util.MemInfoReader;
-import com.android.internal.util.Preconditions;
-import com.android.server.AppOpsService;
-import com.android.server.AttributeCache;
-import com.android.server.DeviceIdleController;
-import com.android.server.IntentResolver;
-import com.android.server.LocalServices;
-import com.android.server.LockGuard;
-import com.android.server.ServiceThread;
-import com.android.server.SystemService;
-import com.android.server.SystemServiceManager;
-import com.android.server.Watchdog;
-import com.android.server.am.ActivityStack.ActivityState;
-import com.android.server.firewall.IntentFirewall;
-import com.android.server.pm.Installer;
-import com.android.server.pm.Installer.InstallerException;
-import com.android.server.statusbar.StatusBarManagerInternal;
-import com.android.server.vr.VrManagerInternal;
-import com.android.server.wm.WindowManagerService;
-
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
-import android.Manifest;
-import android.Manifest.permission;
-import android.annotation.NonNull;
-import android.annotation.UserIdInt;
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.ActivityManager.RunningTaskInfo;
-import android.app.ActivityManager.StackId;
-import android.app.ActivityManager.StackInfo;
-import android.app.ActivityManager.TaskThumbnailInfo;
-import android.app.ActivityManagerInternal;
-import android.app.ActivityManagerInternal.SleepToken;
-import android.app.ActivityOptions;
-import android.app.ActivityThread;
-import android.app.AlertDialog;
-import android.app.AppGlobals;
-import android.app.AppOpsManager;
-import android.app.ApplicationErrorReport;
-import android.app.BroadcastOptions;
-import android.app.Dialog;
-import android.app.IActivityContainer;
-import android.app.IActivityContainerCallback;
-import android.app.IActivityController;
-import android.app.IAppTask;
-import android.app.IApplicationThread;
-import android.app.IInstrumentationWatcher;
-import android.app.INotificationManager;
-import android.app.IProcessObserver;
-import android.app.IServiceConnection;
-import android.app.IStopUserCallback;
-import android.app.ITaskStackListener;
-import android.app.IUiAutomationConnection;
-import android.app.IUidObserver;
-import android.app.IUserSwitchObserver;
-import android.app.Instrumentation;
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.ProfilerInfo;
-import android.app.admin.DevicePolicyManager;
-import android.app.assist.AssistContent;
-import android.app.assist.AssistStructure;
-import android.app.backup.IBackupManager;
-import android.app.usage.UsageEvents;
-import android.app.usage.UsageStatsManagerInternal;
-import android.appwidget.AppWidgetManager;
-import android.content.ActivityNotFoundException;
-import android.content.BroadcastReceiver;
-import android.content.ClipData;
-import android.content.ComponentCallbacks2;
-import android.content.ComponentName;
-import android.content.ContentProvider;
-import android.content.ContentResolver;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.IContentProvider;
-import android.content.IIntentReceiver;
-import android.content.IIntentSender;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.IntentSender;
-import android.content.pm.ActivityInfo;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.ConfigurationInfo;
-import android.content.pm.IPackageDataObserver;
-import android.content.pm.IPackageManager;
-import android.content.pm.InstrumentationInfo;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.PackageManagerInternal;
-import android.content.pm.ParceledListSlice;
-import android.content.pm.PathPermission;
-import android.content.pm.PermissionInfo;
-import android.content.pm.ProviderInfo;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
-import android.content.pm.UserInfo;
-import android.content.res.CompatibilityInfo;
-import android.content.res.Configuration;
-import android.content.res.Resources;
-import android.database.ContentObserver;
-import android.graphics.Bitmap;
-import android.graphics.Point;
-import android.graphics.Rect;
-import android.location.LocationManager;
-import android.net.Proxy;
-import android.net.ProxyInfo;
-import android.net.Uri;
-import android.os.BatteryStats;
-import android.os.Binder;
-import android.os.Build;
-import android.os.Bundle;
-import android.os.Debug;
-import android.os.DropBoxManager;
-import android.os.Environment;
-import android.os.FactoryTest;
-import android.os.FileObserver;
-import android.os.FileUtils;
-import android.os.Handler;
-import android.os.IBinder;
-import android.os.IPermissionController;
-import android.os.IProcessInfoService;
-import android.os.IProgressListener;
-import android.os.LocaleList;
-import android.os.Looper;
-import android.os.Message;
-import android.os.Parcel;
-import android.os.ParcelFileDescriptor;
-import android.os.PersistableBundle;
-import android.os.PowerManager;
-import android.os.PowerManagerInternal;
-import android.os.Process;
-import android.os.RemoteCallbackList;
-import android.os.RemoteException;
-import android.os.ResultReceiver;
-import android.os.ServiceManager;
-import android.os.ShellCallback;
-import android.os.StrictMode;
-import android.os.SystemClock;
-import android.os.SystemProperties;
-import android.os.Trace;
-import android.os.TransactionTooLargeException;
-import android.os.UpdateLock;
-import android.os.UserHandle;
-import android.os.UserManager;
-import android.os.WorkSource;
-import android.os.storage.IStorageManager;
-import android.os.storage.StorageManagerInternal;
-import android.os.storage.StorageManager;
-import android.provider.Downloads;
-import android.provider.Settings;
-import android.service.autofill.AutoFillService;
-import android.service.voice.IVoiceInteractionSession;
-import android.service.voice.VoiceInteractionManagerInternal;
-import android.service.voice.VoiceInteractionSession;
-import android.telecom.TelecomManager;
-import android.text.format.DateUtils;
-import android.text.format.Time;
-import android.text.style.SuggestionSpan;
-import android.util.ArrayMap;
-import android.util.ArraySet;
-import android.util.AtomicFile;
-import android.util.BootTimingsTraceLog;
-import android.util.DebugUtils;
-import android.util.DisplayMetrics;
-import android.util.EventLog;
-import android.util.Log;
-import android.util.Pair;
-import android.util.PrintWriterPrinter;
-import android.util.Slog;
-import android.util.SparseArray;
-import android.util.SparseIntArray;
-import android.util.TimeUtils;
-import android.util.Xml;
-import android.view.Gravity;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.WindowManager;
-
-import java.io.File;
-import java.io.FileDescriptor;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.PrintWriter;
-import java.io.StringWriter;
-import java.lang.ref.WeakReference;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.concurrent.CountDownLatch;
-
-import dalvik.system.VMRuntime;
-
-import libcore.io.IoUtils;
-import libcore.util.EmptyArray;
-
 import static android.Manifest.permission.CHANGE_CONFIGURATION;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS;
 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;
 import static android.Manifest.permission.MANAGE_ACTIVITY_STACKS;
+import static android.Manifest.permission.READ_FRAME_BUFFER;
 import static android.Manifest.permission.START_TASKS_FROM_RECENTS;
 import static android.app.ActivityManager.DOCKED_STACK_CREATE_MODE_TOP_OR_LEFT;
 import static android.app.ActivityManager.RESIZE_MODE_PRESERVE_WINDOW;
@@ -301,7 +54,6 @@
 import static android.provider.Settings.Global.WAIT_FOR_DEBUGGER;
 import static android.provider.Settings.System.FONT_SCALE;
 import static android.view.Display.DEFAULT_DISPLAY;
-
 import static com.android.internal.util.XmlUtils.readBooleanAttribute;
 import static com.android.internal.util.XmlUtils.readIntAttribute;
 import static com.android.internal.util.XmlUtils.readLongAttribute;
@@ -367,7 +119,6 @@
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
 import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.am.ActivityStackSupervisor.ActivityContainer.FORCE_NEW_TASK_FLAGS;
-import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
 import static com.android.server.am.ActivityStackSupervisor.DEFER_RESUME;
 import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
 import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
@@ -387,6 +138,255 @@
 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT;
 import static org.xmlpull.v1.XmlPullParser.START_TAG;
 
+import android.Manifest;
+import android.Manifest.permission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityManager.RunningTaskInfo;
+import android.app.ActivityManager.StackId;
+import android.app.ActivityManager.StackInfo;
+import android.app.ActivityManager.TaskThumbnailInfo;
+import android.app.ActivityManagerInternal;
+import android.app.ActivityManagerInternal.PictureInPictureArguments;
+import android.app.ActivityManagerInternal.SleepToken;
+import android.app.ActivityOptions;
+import android.app.ActivityThread;
+import android.app.AlertDialog;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.ApplicationErrorReport;
+import android.app.ApplicationThreadConstants;
+import android.app.BroadcastOptions;
+import android.app.ContentProviderHolder;
+import android.app.Dialog;
+import android.app.IActivityContainer;
+import android.app.IActivityContainerCallback;
+import android.app.IActivityController;
+import android.app.IActivityManager;
+import android.app.IAppTask;
+import android.app.IApplicationThread;
+import android.app.IInstrumentationWatcher;
+import android.app.INotificationManager;
+import android.app.IProcessObserver;
+import android.app.IServiceConnection;
+import android.app.IStopUserCallback;
+import android.app.ITaskStackListener;
+import android.app.IUiAutomationConnection;
+import android.app.IUidObserver;
+import android.app.IUserSwitchObserver;
+import android.app.Instrumentation;
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.ProfilerInfo;
+import android.app.RemoteAction;
+import android.app.WaitResult;
+import android.app.admin.DevicePolicyManager;
+import android.app.assist.AssistContent;
+import android.app.assist.AssistStructure;
+import android.app.backup.IBackupManager;
+import android.app.usage.UsageEvents;
+import android.app.usage.UsageStatsManagerInternal;
+import android.appwidget.AppWidgetManager;
+import android.content.ActivityNotFoundException;
+import android.content.BroadcastReceiver;
+import android.content.ClipData;
+import android.content.ComponentCallbacks2;
+import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.IContentProvider;
+import android.content.IIntentReceiver;
+import android.content.IIntentSender;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.ConfigurationInfo;
+import android.content.pm.IPackageDataObserver;
+import android.content.pm.IPackageManager;
+import android.content.pm.InstrumentationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageManagerInternal;
+import android.content.pm.ParceledListSlice;
+import android.content.pm.PathPermission;
+import android.content.pm.PermissionInfo;
+import android.content.pm.ProviderInfo;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.pm.UserInfo;
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.database.ContentObserver;
+import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
+import android.graphics.Point;
+import android.graphics.Rect;
+import android.location.LocationManager;
+import android.net.Proxy;
+import android.net.ProxyInfo;
+import android.net.Uri;
+import android.os.BatteryStats;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Debug;
+import android.os.DropBoxManager;
+import android.os.Environment;
+import android.os.FactoryTest;
+import android.os.FileObserver;
+import android.os.FileUtils;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.IDeviceIdentifiersPolicyService;
+import android.os.IPermissionController;
+import android.os.IProcessInfoService;
+import android.os.IProgressListener;
+import android.os.LocaleList;
+import android.os.Looper;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.PersistableBundle;
+import android.os.PowerManager;
+import android.os.PowerManagerInternal;
+import android.os.Process;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ServiceManager;
+import android.os.ShellCallback;
+import android.os.StrictMode;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.os.Trace;
+import android.os.TransactionTooLargeException;
+import android.os.UpdateLock;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.os.WorkSource;
+import android.os.storage.IStorageManager;
+import android.os.storage.StorageManager;
+import android.os.storage.StorageManagerInternal;
+import android.provider.Downloads;
+import android.provider.Settings;
+import android.service.autofill.AutoFillService;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VoiceInteractionManagerInternal;
+import android.service.voice.VoiceInteractionSession;
+import android.telecom.TelecomManager;
+import android.text.format.DateUtils;
+import android.text.format.Time;
+import android.text.style.SuggestionSpan;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.AtomicFile;
+import android.util.BootTimingsTraceLog;
+import android.util.DebugUtils;
+import android.util.DisplayMetrics;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.Pair;
+import android.util.PrintWriterPrinter;
+import android.util.Slog;
+import android.util.SparseArray;
+import android.util.SparseIntArray;
+import android.util.TimeUtils;
+import android.util.Xml;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.WindowManager;
+
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+
+import com.android.internal.R;
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.app.AssistUtils;
+import com.android.internal.app.DumpHeapActivity;
+import com.android.internal.app.IAppOpsCallback;
+import com.android.internal.app.IAppOpsService;
+import com.android.internal.app.IVoiceInteractor;
+import com.android.internal.app.ProcessMap;
+import com.android.internal.app.SystemUserHomeActivity;
+import com.android.internal.app.procstats.ProcessStats;
+import com.android.internal.os.BackgroundThread;
+import com.android.internal.os.BatteryStatsImpl;
+import com.android.internal.os.IResultReceiver;
+import com.android.internal.os.ProcessCpuTracker;
+import com.android.internal.os.TransferPipe;
+import com.android.internal.os.Zygote;
+import com.android.internal.policy.IKeyguardDismissCallback;
+import com.android.internal.telephony.TelephonyIntents;
+import com.android.internal.util.ArrayUtils;
+import com.android.internal.util.FastPrintWriter;
+import com.android.internal.util.FastXmlSerializer;
+import com.android.internal.util.MemInfoReader;
+import com.android.internal.util.Preconditions;
+import com.android.server.AppOpsService;
+import com.android.server.AttributeCache;
+import com.android.server.DeviceIdleController;
+import com.android.server.IntentResolver;
+import com.android.server.LocalServices;
+import com.android.server.LockGuard;
+import com.android.server.ServiceThread;
+import com.android.server.SystemService;
+import com.android.server.SystemServiceManager;
+import com.android.server.Watchdog;
+import com.android.server.am.ActivityStack.ActivityState;
+import com.android.server.firewall.IntentFirewall;
+import com.android.server.pm.Installer;
+import com.android.server.pm.Installer.InstallerException;
+import com.android.server.statusbar.StatusBarManagerInternal;
+import com.android.server.vr.VrManagerInternal;
+import com.android.server.wm.WindowManagerService;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.lang.ref.WeakReference;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.concurrent.atomic.AtomicLong;
+
+import dalvik.system.VMRuntime;
+import libcore.io.IoUtils;
+import libcore.util.EmptyArray;
+
+import static com.android.server.am.ActivityStackSupervisor.CREATE_IF_NEEDED;
 public class ActivityManagerService extends IActivityManager.Stub
         implements Watchdog.Monitor, BatteryStatsImpl.BatteryCallback {
 
@@ -9654,6 +9654,25 @@
     }
 
     @Override
+    public GraphicBuffer getTaskSnapshot(int taskId) {
+        enforceCallingPermission(READ_FRAME_BUFFER, "getTaskSnapshot()");
+        final long ident = Binder.clearCallingIdentity();
+        try {
+            synchronized (this) {
+                final TaskRecord task = mStackSupervisor.anyTaskForIdLocked(
+                        taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+                if (task == null) {
+                    Slog.w(TAG, "getTaskSnapshot: taskId=" + taskId + " not found");
+                    return null;
+                }
+                return task.getSnapshot();
+            }
+        } finally {
+            Binder.restoreCallingIdentity(ident);
+        }
+    }
+
+    @Override
     public Bitmap getTaskDescriptionIcon(String filePath, int userId) {
         if (userId != UserHandle.getCallingUserId()) {
             enforceCallingPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
@@ -9802,6 +9821,15 @@
             }
             mStackSupervisor.findTaskToMoveToFrontLocked(task, flags, options, "moveTaskToFront",
                     false /* forceNonResizable */);
+
+            final ActivityRecord topActivity = task.getTopActivity();
+            if (topActivity != null) {
+
+                // We are reshowing a task, use a starting window to hide the initial draw delay
+                // so the transition can start earlier.
+                topActivity.showStartingWindow(null /* prev */, false /* newTask */,
+                        true /* taskSwitch */);
+            }
         } finally {
             Binder.restoreCallingIdentity(origId);
         }
diff --git a/services/core/java/com/android/server/am/ActivityRecord.java b/services/core/java/com/android/server/am/ActivityRecord.java
index 2963ad1..47c3e6f 100644
--- a/services/core/java/com/android/server/am/ActivityRecord.java
+++ b/services/core/java/com/android/server/am/ActivityRecord.java
@@ -16,6 +16,7 @@
 
 package com.android.server.am;
 
+import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
 import static android.app.ActivityManager.StackId;
 import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
 import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID;
@@ -26,11 +27,11 @@
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_LAYOUT;
 import static android.content.pm.ActivityInfo.CONFIG_SCREEN_SIZE;
 import static android.content.pm.ActivityInfo.CONFIG_SMALLEST_SCREEN_SIZE;
+import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.content.pm.ActivityInfo.FLAG_EXCLUDE_FROM_RECENTS;
 import static android.content.pm.ActivityInfo.FLAG_IMMERSIVE;
 import static android.content.pm.ActivityInfo.FLAG_MULTIPROCESS;
 import static android.content.pm.ActivityInfo.FLAG_SHOW_FOR_ALL_USERS;
-import static android.content.pm.ActivityInfo.FLAG_ALWAYS_FOCUSABLE;
 import static android.content.pm.ActivityInfo.FLAG_STATE_NOT_NEEDED;
 import static android.content.pm.ActivityInfo.LAUNCH_MULTIPLE;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
@@ -103,6 +104,10 @@
 import com.android.server.wm.AppWindowContainerListener;
 import com.android.server.wm.TaskWindowContainerController;
 
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+import org.xmlpull.v1.XmlSerializer;
+
 import java.io.File;
 import java.io.IOException;
 import java.io.PrintWriter;
@@ -113,10 +118,6 @@
 import java.util.List;
 import java.util.Objects;
 
-import org.xmlpull.v1.XmlPullParser;
-import org.xmlpull.v1.XmlPullParserException;
-import org.xmlpull.v1.XmlSerializer;
-
 /**
  * An entry in the history stack, representing an activity.
  */
@@ -1204,6 +1205,14 @@
 
     final Bitmap screenshotActivityLocked() {
         if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS, "screenshotActivityLocked: " + this);
+
+        if (ENABLE_TASK_SNAPSHOTS) {
+            // No need to screenshot if snapshots are enabled.
+            if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS,
+                    "\tSnapshots are enabled, abort taking screenshot");
+            return null;
+        }
+
         if (noDisplay) {
             if (DEBUG_SCREENSHOTS) Slog.d(TAG_SCREENSHOTS, "\tNo display");
             return null;
@@ -1792,12 +1801,12 @@
         pendingVoiceInteractionStart = false;
     }
 
-    void showStartingWindow(ActivityRecord prev, boolean createIfNeeded) {
+    void showStartingWindow(ActivityRecord prev, boolean newTask, boolean taskSwitch) {
         final CompatibilityInfo compatInfo =
                 service.compatibilityInfoForPackageLocked(info.applicationInfo);
         final boolean shown = mWindowContainerController.addStartingWindow(packageName, theme,
                 compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
-                prev != null ? prev.appToken : null, createIfNeeded);
+                prev != null ? prev.appToken : null, newTask, taskSwitch, isProcessRunning());
         if (shown) {
             mStartingWindowState = STARTING_WINDOW_SHOWN;
         }
@@ -2089,6 +2098,14 @@
         preserveWindowOnDeferredRelaunch = false;
     }
 
+    boolean isProcessRunning() {
+        ProcessRecord proc = app;
+        if (proc == null) {
+            proc = service.mProcessNames.get(processName, info.applicationInfo.uid);
+        }
+        return proc != null && proc.thread != null;
+    }
+
     void saveToXml(XmlSerializer out) throws IOException, XmlPullParserException {
         out.attribute(null, ATTR_ID, String.valueOf(createTime));
         out.attribute(null, ATTR_LAUNCHEDFROMUID, String.valueOf(launchedFromUid));
diff --git a/services/core/java/com/android/server/am/ActivityStack.java b/services/core/java/com/android/server/am/ActivityStack.java
index ab65eb1..abcaa24 100644
--- a/services/core/java/com/android/server/am/ActivityStack.java
+++ b/services/core/java/com/android/server/am/ActivityStack.java
@@ -2459,7 +2459,8 @@
                     next.hasBeenLaunched = true;
                 } else  if (SHOW_APP_STARTING_PREVIEW && lastStack != null &&
                         mStackSupervisor.isFrontStack(lastStack)) {
-                    next.showStartingWindow(null, true);
+                    next.showStartingWindow(null /* prev */, false /* newTask */,
+                            false /* taskSwitch */);
                 }
                 mStackSupervisor.startSpecificActivityLocked(next, true, false);
                 if (DEBUG_STACK) mStackSupervisor.validateTopActivitiesLocked();
@@ -2485,7 +2486,8 @@
                 next.hasBeenLaunched = true;
             } else {
                 if (SHOW_APP_STARTING_PREVIEW) {
-                    next.showStartingWindow(null, true);
+                    next.showStartingWindow(null /* prev */, false /* newTask */,
+                            false /* taskSwich */);
                 }
                 if (DEBUG_SWITCH) Slog.v(TAG_SWITCH, "Restarting: " + next);
             }
@@ -2686,17 +2688,6 @@
         task.setFrontOfTask();
 
         if (!isHomeOrRecentsStack() || numActivities() > 0) {
-            // We want to show the starting preview window if we are
-            // switching to a new task, or the next activity's process is
-            // not currently running.
-            boolean showStartingIcon = newTask;
-            ProcessRecord proc = r.app;
-            if (proc == null) {
-                proc = mService.mProcessNames.get(r.processName, r.info.applicationInfo.uid);
-            }
-            if (proc == null || proc.thread == null) {
-                showStartingIcon = true;
-            }
             if (DEBUG_TRANSITION) Slog.v(TAG_TRANSITION,
                     "Prepare open transition: starting " + r);
             if ((r.intent.getFlags() & Intent.FLAG_ACTIVITY_NO_ANIMATION) != 0) {
@@ -2756,7 +2747,7 @@
                         prev = null;
                     }
                 }
-                r.showStartingWindow(prev, showStartingIcon);
+                r.showStartingWindow(prev, newTask, isTaskSwitch(r, focusedTopActivity));
             }
         } else {
             // If this is the first activity, don't do any fancy animations,
@@ -2765,6 +2756,11 @@
         }
     }
 
+    private boolean isTaskSwitch(ActivityRecord r,
+            ActivityRecord topFocusedActivity) {
+        return topFocusedActivity != null && r.task != topFocusedActivity.task;
+    }
+
     /**
      * Perform a reset of the given task, if needed as part of launching it.
      * Returns the new HistoryRecord at the top of the task.
diff --git a/services/core/java/com/android/server/am/ActivityStarter.java b/services/core/java/com/android/server/am/ActivityStarter.java
index 61e3ad5..f401863 100644
--- a/services/core/java/com/android/server/am/ActivityStarter.java
+++ b/services/core/java/com/android/server/am/ActivityStarter.java
@@ -31,8 +31,8 @@
 import static android.app.ActivityManager.StackId.HOME_STACK_ID;
 import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
 import static android.app.ActivityManager.StackId.PINNED_STACK_ID;
-import static android.app.ActivityManager.StackId.isStaticStack;
 import static android.app.ActivityManager.StackId.RECENTS_STACK_ID;
+import static android.app.ActivityManager.StackId.isStaticStack;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
 import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
 import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
@@ -53,7 +53,6 @@
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TASK;
 import static android.content.pm.ActivityInfo.LAUNCH_SINGLE_TOP;
 import static android.view.Display.INVALID_DISPLAY;
-
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_CONFIGURATION;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_FOCUS;
 import static com.android.server.am.ActivityManagerDebugConfig.DEBUG_PERMISSIONS_REVIEW;
@@ -106,7 +105,6 @@
 import android.os.Binder;
 import android.os.Bundle;
 import android.os.IBinder;
-import android.os.PowerManagerInternal;
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.os.UserHandle;
@@ -1509,6 +1507,11 @@
                         mMovedToFront = true;
                     }
                     mOptions = null;
+
+                    // We are moving a task to the front, use starting window to hide initial drawn
+                    // delay.
+                    intentActivity.showStartingWindow(null /* prev */, false /* newTask */,
+                            true /* taskSwitch */);
                 }
                 updateTaskReturnToType(intentActivity.task, mLaunchFlags, focusStack);
             }
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index d0eac77..4e44c5f 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -34,6 +34,7 @@
 import android.content.pm.PackageManager;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.os.Debug;
@@ -545,6 +546,13 @@
         mWindowContainerController.cancelThumbnailTransition();
     }
 
+    public GraphicBuffer getSnapshot() {
+        if (mWindowContainerController == null) {
+            return null;
+        }
+        return mWindowContainerController.getSnapshot();
+    }
+
     void touchActiveTime() {
         lastActiveTime = System.currentTimeMillis();
         if (firstActiveTime == 0) {
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 0a312f0..4b2b184 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -51,6 +51,7 @@
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_FORCE_STATUS_BAR_VISIBLE_TRANSPARENT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_KEYGUARD;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_SYSTEM_ERROR;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_CROSSFADE;
 import static android.view.WindowManager.LayoutParams.ROTATION_ANIMATION_JUMPCUT;
@@ -2907,7 +2908,7 @@
 
             // Only return the view if it was successfully added to the
             // window manager... which we can tell by it having a parent.
-            return view.getParent() != null ? new SplashScreenSurface(view) : null;
+            return view.getParent() != null ? new SplashScreenSurface(view, appToken) : null;
         } catch (WindowManager.BadTokenException e) {
             // ignore
             Log.w(TAG, appToken + " already running, starting window not displayed. " +
@@ -2927,18 +2928,6 @@
         return null;
     }
 
-    /** {@inheritDoc} */
-    @Override
-    public void removeSplashScreen(IBinder appToken, StartingSurface surface) {
-        if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + appToken + ": "
-                + surface + " Callers=" + Debug.getCallers(4));
-
-        if (surface != null) {
-            WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
-            wm.removeView(((SplashScreenSurface) surface).view);
-        }
-    }
-
     /**
      * Preflight adding a window to the system.
      *
@@ -5185,7 +5174,7 @@
     @Override
     public void applyPostLayoutPolicyLw(WindowState win, WindowManager.LayoutParams attrs,
             WindowState attached, WindowState imeTarget) {
-        final boolean visible = win.isVisibleLw();
+        final boolean visible = !win.isGoneForLayoutLw();
         if (DEBUG_LAYOUT) Slog.i(TAG, "Win " + win + ": isVisible=" + visible);
         applyKeyguardPolicyLw(win, imeTarget);
         final int fl = PolicyControl.getWindowFlags(win, attrs);
@@ -5202,8 +5191,11 @@
             }
         }
 
+        // Don't allow snapshots to influence SystemUI visibility flags.
+        // TODO: Revisit this once SystemUI flags for snapshots are handled correctly
         boolean appWindow = attrs.type >= FIRST_APPLICATION_WINDOW
-                && attrs.type < FIRST_SYSTEM_WINDOW;
+                && attrs.type < FIRST_SYSTEM_WINDOW
+                && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0;
         final int stackId = win.getStackId();
         if (mTopFullscreenOpaqueWindowState == null && visible) {
             if ((fl & FLAG_FORCE_NOT_FULLSCREEN) != 0) {
diff --git a/services/core/java/com/android/server/policy/SplashScreenSurface.java b/services/core/java/com/android/server/policy/SplashScreenSurface.java
index d421291..37d6c0b 100644
--- a/services/core/java/com/android/server/policy/SplashScreenSurface.java
+++ b/services/core/java/com/android/server/policy/SplashScreenSurface.java
@@ -16,7 +16,13 @@
 
 package com.android.server.policy;
 
+import static com.android.server.policy.PhoneWindowManager.DEBUG_SPLASH_SCREEN;
+
+import android.os.Debug;
+import android.os.IBinder;
+import android.util.Slog;
 import android.view.View;
+import android.view.WindowManager;
 import android.view.WindowManagerPolicy;
 import android.view.WindowManagerPolicy.StartingSurface;
 
@@ -30,9 +36,21 @@
  */
 class SplashScreenSurface implements StartingSurface {
 
-    final View view;
+    private static final String TAG = PhoneWindowManager.TAG;
+    private final View mView;
+    private final IBinder mAppToken;
 
-    SplashScreenSurface(View view) {
-        this.view = view;
+    SplashScreenSurface(View view, IBinder appToken) {
+        mView = view;
+        mAppToken = appToken;
+    }
+
+    @Override
+    public void remove() {
+        if (DEBUG_SPLASH_SCREEN) Slog.v(TAG, "Removing splash screen window for " + mAppToken + ": "
+                        + this + " Callers=" + Debug.getCallers(4));
+
+        final WindowManager wm = mView.getContext().getSystemService(WindowManager.class);
+        wm.removeView(mView);
     }
 }
diff --git a/services/core/java/com/android/server/wm/AppWindowContainerController.java b/services/core/java/com/android/server/wm/AppWindowContainerController.java
index d2f604d..4dbe35f 100644
--- a/services/core/java/com/android/server/wm/AppWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/AppWindowContainerController.java
@@ -31,6 +31,7 @@
 import android.content.res.CompatibilityInfo;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.os.Debug;
 import android.os.Handler;
 import android.os.IBinder;
@@ -50,6 +51,10 @@
 public class AppWindowContainerController
         extends WindowContainerController<AppWindowToken, AppWindowContainerListener> {
 
+    private static final int STARTING_WINDOW_TYPE_NONE = 0;
+    private static final int STARTING_WINDOW_TYPE_SNAPSHOT = 1;
+    private static final int STARTING_WINDOW_TYPE_SPLASH_SCREEN = 2;
+
     private final IApplicationToken mToken;
     private final Handler mHandler = new Handler(Looper.getMainLooper());
 
@@ -80,16 +85,39 @@
         mListener.onWindowsGone();
     };
 
+    private final Runnable mRemoveStartingWindow = () -> {
+        StartingSurface surface = null;
+        StartingData data = null;
+        synchronized (mWindowMap) {
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting " + mContainer
+                    + ": startingWindow=" + mContainer.startingWindow
+                    + " startingView=" + mContainer.startingSurface);
+            if (mContainer.startingWindow != null) {
+                surface = mContainer.startingSurface;
+                data = mContainer.startingData;
+                mContainer.startingData = null;
+                mContainer.startingSurface = null;
+                mContainer.startingWindow = null;
+                mContainer.startingDisplayed = false;
+            }
+        }
+        if (data != null && surface != null) {
+            try {
+                surface.remove();
+            } catch (Exception e) {
+                Slog.w(TAG_WM, "Exception when removing starting window", e);
+            }
+        }
+    };
+
     private final Runnable mAddStartingWindow = () -> {
         final StartingData startingData;
-        final Configuration mergedOverrideConfiguration;
 
         synchronized (mWindowMap) {
             if (mContainer == null) {
                 return;
             }
             startingData = mContainer.startingData;
-            mergedOverrideConfiguration = mContainer.getMergedOverrideConfiguration();
         }
 
         if (startingData == null) {
@@ -98,20 +126,16 @@
         }
 
         if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Add starting "
-                + this + ": pkg=" + mContainer.startingData.pkg);
+                + this + ": startingData=" + mContainer.startingData);
 
-        StartingSurface contents = null;
+        StartingSurface surface = null;
         try {
-            contents = mService.mPolicy.addSplashScreen(mContainer.token, startingData.pkg,
-                    startingData.theme, startingData.compatInfo, startingData.nonLocalizedLabel,
-                    startingData.labelRes, startingData.icon, startingData.logo,
-                    startingData.windowFlags, mergedOverrideConfiguration);
+            surface = startingData.createStartingSurface();
         } catch (Exception e) {
             Slog.w(TAG_WM, "Exception when adding starting window", e);
         }
-        if (contents != null) {
+        if (surface != null) {
             boolean abort = false;
-
             synchronized(mWindowMap) {
                 if (mContainer.removed || mContainer.startingData == null) {
                     // If the window was successfully added, then
@@ -121,12 +145,10 @@
                                 "Aborted starting " + mContainer
                                         + ": removed=" + mContainer.removed
                                         + " startingData=" + mContainer.startingData);
-                        mContainer.startingWindow = null;
-                        mContainer.startingData = null;
                         abort = true;
                     }
                 } else {
-                    mContainer.startingSurface = contents;
+                    mContainer.startingSurface = surface;
                 }
                 if (DEBUG_STARTING_WINDOW && !abort) Slog.v(TAG_WM,
                         "Added starting " + mContainer
@@ -134,42 +156,11 @@
                                 + mContainer.startingWindow + " startingView="
                                 + mContainer.startingSurface);
             }
-
             if (abort) {
-                try {
-                    mService.mPolicy.removeSplashScreen(mContainer.token, contents);
-                } catch (Exception e) {
-                    Slog.w(TAG_WM, "Exception when removing starting window", e);
-                }
-            }
-        }
-    };
-
-    private final Runnable mRemoveStartingWindow = () -> {
-        IBinder token = null;
-        StartingSurface contents = null;
-        synchronized (mWindowMap) {
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Remove starting "
-                    + mContainer + ": startingWindow="
-                    + mContainer.startingWindow + " startingView="
-                    + mContainer.startingSurface);
+                mRemoveStartingWindow.run();
             if (mContainer == null) {
                 return;
             }
-            if (mContainer.startingWindow != null) {
-                contents = mContainer.startingSurface;
-                token = mContainer.token;
-                mContainer.startingData = null;
-                mContainer.startingSurface = null;
-                mContainer.startingWindow = null;
-                mContainer.startingDisplayed = false;
-            }
-        }
-        if (contents != null) {
-            try {
-                mService.mPolicy.removeSplashScreen(token, contents);
-            } catch (Exception e) {
-                Slog.w(TAG_WM, "Exception when removing starting window", e);
             }
         }
     };
@@ -389,7 +380,7 @@
 
     public boolean addStartingWindow(String pkg, int theme, CompatibilityInfo compatInfo,
             CharSequence nonLocalizedLabel, int labelRes, int icon, int logo, int windowFlags,
-            IBinder transferFrom, boolean createIfNeeded) {
+            IBinder transferFrom, boolean newTask, boolean taskSwitch, boolean processRunning) {
         synchronized(mWindowMap) {
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "setAppStartingWindow: token=" + mToken
                     + " pkg=" + pkg + " transferFrom=" + transferFrom);
@@ -409,6 +400,12 @@
                 return false;
             }
 
+            final int type = getStartingWindowType(newTask, taskSwitch, processRunning);
+
+            if (type == STARTING_WINDOW_TYPE_SNAPSHOT) {
+                return createSnapshot();
+            }
+
             // If this is a translucent window, then don't show a starting window -- the current
             // effect (a full-screen opaque starting window that fades away to the real contents
             // when it is ready) does not work for this.
@@ -458,22 +455,32 @@
                 return true;
             }
 
-            // There is no existing starting window, and the caller doesn't
-            // want us to create one, so that's it!
-            if (!createIfNeeded) {
+            // There is no existing starting window, and we don't want to create a splash screen, so
+            // that's it!
+            if (type != STARTING_WINDOW_TYPE_SPLASH_SCREEN) {
                 return false;
             }
 
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Creating StartingData");
-            mContainer.startingData = new StartingData(pkg, theme, compatInfo, nonLocalizedLabel,
-                    labelRes, icon, logo, windowFlags);
+            mContainer.startingData = new SplashScreenStartingData(mService, mContainer, pkg, theme,
+                    compatInfo, nonLocalizedLabel, labelRes, icon, logo, windowFlags,
+                    mContainer.getMergedOverrideConfiguration());
             scheduleAddStartingWindow();
         }
         return true;
     }
 
-    void scheduleAddStartingWindow() {
+    private int getStartingWindowType(boolean newTask, boolean taskSwitch, boolean processRunning) {
+        if (newTask || !processRunning) {
+            return STARTING_WINDOW_TYPE_SPLASH_SCREEN;
+        } else if (taskSwitch) {
+            return STARTING_WINDOW_TYPE_SNAPSHOT;
+        } else {
+            return STARTING_WINDOW_TYPE_NONE;
+        }
+    }
 
+    void scheduleAddStartingWindow() {
         // Note: we really want to do sendMessageAtFrontOfQueue() because we
         // want to process the message ASAP, before any other queued
         // messages.
@@ -481,6 +488,19 @@
         mHandler.postAtFrontOfQueue(mAddStartingWindow);
     }
 
+    private boolean createSnapshot() {
+        final GraphicBuffer snapshot = mService.mTaskSnapshotController.getSnapshot(
+                mContainer.mTask);
+
+        if (snapshot == null) {
+            return false;
+        }
+
+        mContainer.startingData = new SnapshotStartingData(mService, mContainer, snapshot);
+        scheduleAddStartingWindow();
+        return true;
+    }
+
     public void removeStartingWindow() {
         synchronized (mWindowMap) {
             if (mHandler.hasCallbacks(mRemoveStartingWindow)) {
@@ -593,7 +613,7 @@
             }
             return dc.screenshotApplications(mToken.asBinder(), width, height,
                     false /* includeFullDisplay */, frameScale, Bitmap.Config.RGB_565,
-                    false /* wallpaperOnly */);
+                    false /* wallpaperOnly */, false /* includeDecor */, true /* toAshmem */);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index f4fa220..10d1d8b 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -205,7 +205,7 @@
         // We now have a good window to show, remove dead placeholders
         removeDeadWindows();
 
-        if (startingData != null) {
+        if (startingWindow != null) {
             if (DEBUG_STARTING_WINDOW || DEBUG_ANIM) Slog.v(TAG, "Finish starting "
                     + win.mToken + ": first real window is shown, no animation");
             // If this initial window is animating, stop it -- we will do an animation to reveal
@@ -671,7 +671,7 @@
         } else if (mChildren.size() == 0 && startingData != null) {
             // If this is the last window and we had requested a starting transition window,
             // well there is no point now.
-            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingWindow");
+            if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData");
             startingData = null;
         } else if (mChildren.size() == 1 && startingSurface != null) {
             // If this is the last window except for a starting transition window,
diff --git a/services/core/java/com/android/server/wm/DisplayContent.java b/services/core/java/com/android/server/wm/DisplayContent.java
index d86c4da..66267bd 100644
--- a/services/core/java/com/android/server/wm/DisplayContent.java
+++ b/services/core/java/com/android/server/wm/DisplayContent.java
@@ -106,6 +106,7 @@
 import android.os.RemoteException;
 import android.os.SystemClock;
 import android.util.DisplayMetrics;
+import android.util.MutableBoolean;
 import android.util.Slog;
 import android.view.Display;
 import android.view.DisplayInfo;
@@ -2102,10 +2103,14 @@
      * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
      * @param config of the output bitmap
      * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
+     * @param includeDecor whether to include window decors, like the status or navigation bar
+     *                     background of the window
+     * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to
+     *                 true if the Bitmap is sent over binder, and false otherwise
      */
     Bitmap screenshotApplications(IBinder appToken, int width, int height,
             boolean includeFullDisplay, float frameScale, Bitmap.Config config,
-            boolean wallpaperOnly) {
+            boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) {
         int dw = mDisplayInfo.logicalWidth;
         int dh = mDisplayInfo.logicalHeight;
         if (dw == 0 || dh == 0) {
@@ -2137,7 +2142,7 @@
 
         final int aboveAppLayer = (mService.mPolicy.windowTypeToLayerLw(TYPE_APPLICATION) + 1)
                 * TYPE_LAYER_MULTIPLIER + TYPE_LAYER_OFFSET;
-
+        final MutableBoolean mutableIncludeFullDisplay = new MutableBoolean(includeFullDisplay);
         synchronized(mService.mWindowMap) {
             // Figure out the part of the screen that is actually the app.
             mScreenshotApplicationState.appWin = null;
@@ -2194,7 +2199,11 @@
                 }
 
                 // Don't include wallpaper in bounds calculation
-                if (!includeFullDisplay && !w.mIsWallpaper) {
+                if (includeDecor && !stackBounds.isEmpty()) {
+                    frame.set(stackBounds);
+                } else if (includeDecor) {
+                    mutableIncludeFullDisplay.value = true;
+                } else if (!mutableIncludeFullDisplay.value && !w.mIsWallpaper) {
                     final Rect wf = w.mFrame;
                     final Rect cr = w.mContentInsets;
                     int left = wf.left + cr.left;
@@ -2252,7 +2261,7 @@
                 return null;
             }
 
-            if (!includeFullDisplay) {
+            if (!mutableIncludeFullDisplay.value) {
                 // Constrain frame to the screen size.
                 if (!frame.intersect(0, 0, dw, dh)) {
                     frame.setEmpty();
@@ -2353,9 +2362,13 @@
 
         // Create a copy of the screenshot that is immutable and backed in ashmem.
         // This greatly reduces the overhead of passing the bitmap between processes.
-        Bitmap ret = bm.createAshmemBitmap(config);
-        bm.recycle();
-        return ret;
+        if (toAshmem) {
+            Bitmap ret = bm.createAshmemBitmap(config);
+            bm.recycle();
+            return ret;
+        } else {
+            return bm;
+        }
     }
 
     // TODO: Can this use createRotationMatrix()?
diff --git a/services/core/java/com/android/server/wm/SnapshotStartingData.java b/services/core/java/com/android/server/wm/SnapshotStartingData.java
new file mode 100644
index 0000000..9d97a0c
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SnapshotStartingData.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.graphics.GraphicBuffer;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+/**
+ * Represents starting data for snapshot starting windows.
+ */
+class SnapshotStartingData extends StartingData {
+
+    private final WindowManagerService mService;
+    private final GraphicBuffer mSnapshot;
+
+    SnapshotStartingData(WindowManagerService service, AppWindowToken appWindowToken,
+            GraphicBuffer snapshot) {
+        super(service, appWindowToken);
+        mService = service;
+        mSnapshot = snapshot;
+    }
+
+    @Override
+    StartingSurface createStartingSurface() {
+        return mService.mTaskSnapshotController.createStartingSurface(
+                mAppWindowToken, mSnapshot);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/SplashScreenStartingData.java b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
new file mode 100644
index 0000000..664e600
--- /dev/null
+++ b/services/core/java/com/android/server/wm/SplashScreenStartingData.java
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.content.res.CompatibilityInfo;
+import android.content.res.Configuration;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+/**
+ * Represents starting data for splash screens, i.e. "traditional" starting windows.
+ */
+class SplashScreenStartingData extends StartingData {
+
+    private final String mPkg;
+    private final int mTheme;
+    private final CompatibilityInfo mCompatInfo;
+    private final CharSequence mNonLocalizedLabel;
+    private final int mLabelRes;
+    private final int mIcon;
+    private final int mLogo;
+    private final int mWindowFlags;
+    private final Configuration mMergedOverrideConfiguration;
+
+    SplashScreenStartingData(WindowManagerService service, AppWindowToken appWindowToken,
+            String pkg, int theme, CompatibilityInfo compatInfo, CharSequence nonLocalizedLabel,
+            int labelRes, int icon, int logo, int windowFlags,
+            Configuration mergedOverrideConfiguration) {
+        super(service, appWindowToken);
+        mPkg = pkg;
+        mTheme = theme;
+        mCompatInfo = compatInfo;
+        mNonLocalizedLabel = nonLocalizedLabel;
+        mLabelRes = labelRes;
+        mIcon = icon;
+        mLogo = logo;
+        mWindowFlags = windowFlags;
+        mMergedOverrideConfiguration = mergedOverrideConfiguration;
+    }
+
+    @Override
+    StartingSurface createStartingSurface() {
+        return mService.mPolicy.addSplashScreen(mAppWindowToken.token, mPkg, mTheme, mCompatInfo,
+                mNonLocalizedLabel, mLabelRes, mIcon, mLogo, mWindowFlags,
+                mMergedOverrideConfiguration);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index 7115b0f..fcc4c3c 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -16,28 +16,27 @@
 
 package com.android.server.wm;
 
-import android.content.res.CompatibilityInfo;
+import android.view.WindowManagerPolicy.StartingSurface;
 
-final class StartingData {
-    final String pkg;
-    final int theme;
-    final CompatibilityInfo compatInfo;
-    final CharSequence nonLocalizedLabel;
-    final int labelRes;
-    final int icon;
-    final int logo;
-    final int windowFlags;
+/**
+ * Represents the model about how a starting window should be constructed.
+ */
+public abstract class StartingData {
 
-    StartingData(String _pkg, int _theme, CompatibilityInfo _compatInfo,
-            CharSequence _nonLocalizedLabel,
-            int _labelRes, int _icon, int _logo, int _windowFlags) {
-        pkg = _pkg;
-        theme = _theme;
-        compatInfo = _compatInfo;
-        nonLocalizedLabel = _nonLocalizedLabel;
-        labelRes = _labelRes;
-        icon = _icon;
-        logo = _logo;
-        windowFlags = _windowFlags;
+    protected final WindowManagerService mService;
+    protected final AppWindowToken mAppWindowToken;
+
+    protected StartingData(WindowManagerService service, AppWindowToken appWindowToken) {
+        mService = service;
+        mAppWindowToken = appWindowToken;
     }
+
+    /**
+     * Creates the actual starting window surface. DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING
+     * THIS METHOD.
+     *
+     * @return a class implementing {@link StartingSurface} for easy removal with
+     *         {@link StartingSurface#remove}
+     */
+    abstract StartingSurface createStartingSurface();
 }
\ No newline at end of file
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotCache.java b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
new file mode 100644
index 0000000..b4fa5a5
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskSnapshotCache.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import android.annotation.Nullable;
+import android.graphics.GraphicBuffer;
+import android.util.ArrayMap;
+
+/**
+ * Caches snapshots. See {@link TaskSnapshotController}.
+ * <p>
+ * Access to this class should be guarded by the global window manager lock.
+ */
+class TaskSnapshotCache {
+
+    private final ArrayMap<Task, GraphicBuffer> mCache = new ArrayMap<>();
+
+    void putSnapshot(Task task, GraphicBuffer snapshot) {
+        mCache.put(task, snapshot);
+    }
+
+    @Nullable GraphicBuffer getSnapshot(Task task) {
+        return mCache.get(task);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
new file mode 100644
index 0000000..d08c744
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
+import static android.graphics.Bitmap.Config.ARGB_8888;
+import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE;
+import static android.graphics.GraphicBuffer.USAGE_SW_READ_NEVER;
+import static android.graphics.GraphicBuffer.USAGE_SW_WRITE_NEVER;
+import static android.graphics.PixelFormat.RGBA_8888;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager.StackId;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.GraphicBuffer;
+import android.util.ArraySet;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+/**
+ * When an app token becomes invisible, we take a snapshot (bitmap) of the corresponding task and
+ * put it into our cache. Internally we use gralloc buffers to be able to draw them wherever we
+ * like without any copying.
+ * <p>
+ * System applications may retrieve a snapshot to represent the current state of a task, and draw
+ * them in their own process.
+ * <p>
+ * When we task becomes visible again, we show a starting window with the snapshot as the content to
+ * make app transitions more responsive.
+ * <p>
+ * To access this class, acquire the global window manager lock.
+ */
+class TaskSnapshotController {
+
+    private final WindowManagerService mService;
+    private final TaskSnapshotCache mCache = new TaskSnapshotCache();
+
+    private final ArraySet<Task> mTmpTasks = new ArraySet<>();
+
+    TaskSnapshotController(WindowManagerService service) {
+        mService = service;
+    }
+
+    void onTransitionStarting() {
+        if (!ENABLE_TASK_SNAPSHOTS) {
+            return;
+        }
+
+        // We need to take a snapshot of the task if and only if all activities of the task are
+        // either closing or hidden.
+        getClosingTasks(mService.mClosingApps, mTmpTasks);
+        for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
+            final Task task = mTmpTasks.valueAt(i);
+            if (!canSnapshotTask(task)) {
+                continue;
+            }
+            final GraphicBuffer graphicBuffer = snapshotTask(task);
+            if (graphicBuffer != null) {
+                mCache.putSnapshot(task, graphicBuffer);
+            }
+        }
+    }
+
+    @Nullable GraphicBuffer getSnapshot(Task task) {
+        return mCache.getSnapshot(task);
+    }
+
+    /**
+     * Creates a starting surface for {@param token} with {@param snapshot}. DO NOT HOLD THE WINDOW
+     * MANAGER LOCK WHEN CALLING THIS METHOD!
+     */
+    StartingSurface createStartingSurface(AppWindowToken token,
+            GraphicBuffer snapshot) {
+        return TaskSnapshotSurface.create(mService, token, snapshot);
+    }
+
+    private GraphicBuffer snapshotTask(Task task) {
+        final AppWindowToken top = (AppWindowToken) task.getTop();
+        if (top == null) {
+            return null;
+        }
+        final Bitmap bmp = top.mDisplayContent.screenshotApplications(top.token, -1, -1, false,
+                1.0f, ARGB_8888, false, true, false);
+        if (bmp == null) {
+            return null;
+        }
+        // TODO: Already use a GraphicBuffer when snapshotting the content.
+        final GraphicBuffer buffer = GraphicBuffer.create(bmp.getWidth(), bmp.getHeight(),
+                RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_NEVER | USAGE_SW_READ_NEVER);
+        final Canvas c = buffer.lockCanvas();
+        c.drawBitmap(bmp, 0, 0, null);
+        buffer.unlockCanvasAndPost(c);
+        return buffer;
+    }
+
+    /**
+     * Retrieves all closing tasks based on the list of closing apps during an app transition.
+     */
+    @VisibleForTesting
+    void getClosingTasks(ArraySet<AppWindowToken> closingApps, ArraySet<Task> outClosingTasks) {
+        outClosingTasks.clear();
+        for (int i = closingApps.size() - 1; i >= 0; i--) {
+            final AppWindowToken atoken = closingApps.valueAt(i);
+
+            // If the task of the app is not visible anymore, it means no other app in that task
+            // is opening. Thus, the task is closing.
+            if (atoken.mTask != null && !atoken.mTask.isVisible()) {
+                outClosingTasks.add(closingApps.valueAt(i).mTask);
+            }
+        }
+    }
+
+    private boolean canSnapshotTask(Task task) {
+        return !StackId.isHomeOrRecentsStack(task.mStack.mStackId);
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
new file mode 100644
index 0000000..c3e3141
--- /dev/null
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+
+import android.content.res.Configuration;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.GraphicBuffer;
+import android.graphics.Rect;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Slog;
+import android.view.Display;
+import android.view.IWindowSession;
+import android.view.Surface;
+import android.view.View;
+import android.view.ViewGroup.LayoutParams;
+import android.view.WindowManager;
+import android.view.WindowManagerGlobal;
+import android.view.WindowManagerPolicy.StartingSurface;
+
+import com.android.internal.view.BaseIWindow;
+
+/**
+ * This class represents a starting window that shows a snapshot.
+ * <p>
+ * DO NOT HOLD THE WINDOW MANAGER LOCK WHEN CALLING METHODS OF THIS CLASS!
+ */
+class TaskSnapshotSurface implements StartingSurface {
+
+    private static final String TAG = TAG_WITH_CLASS_NAME ? "SnapshotStartingWindow" : TAG_WM;
+    private static final int MSG_REPORT_DRAW = 0;
+    private static final String TITLE_FORMAT = "SnapshotStartingWindow for taskId=%s";
+    private final Window mWindow;
+    private final Surface mSurface;
+    private final IWindowSession mSession;
+    private final WindowManagerService mService;
+    private boolean mHasDrawn;
+    private boolean mReportNextDraw;
+
+    static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
+            GraphicBuffer snapshot) {
+
+        final WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams();
+        final Window window = new Window();
+        final IWindowSession session = WindowManagerGlobal.getWindowSession();
+        window.setSession(session);
+        final Surface surface = new Surface();
+        final Rect tmpRect = new Rect();
+        final Rect tmpFrame = new Rect();
+        final Configuration tmpConfiguration = new Configuration();
+        synchronized (service.mWindowMap) {
+            layoutParams.type = TYPE_APPLICATION_STARTING;
+            layoutParams.format = snapshot.getFormat();
+            layoutParams.flags = FLAG_LAYOUT_INSET_DECOR
+                    | FLAG_LAYOUT_IN_SCREEN
+                    | FLAG_NOT_FOCUSABLE
+                    | FLAG_NOT_TOUCHABLE
+                    | FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
+            layoutParams.privateFlags = PRIVATE_FLAG_TASK_SNAPSHOT;
+            layoutParams.token = token.token;
+            layoutParams.width = LayoutParams.MATCH_PARENT;
+            layoutParams.height = LayoutParams.MATCH_PARENT;
+
+            // TODO: Inherit behavior whether to draw behind status bar/nav bar.
+            layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+                    | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
+            layoutParams.setTitle(String.format(TITLE_FORMAT, token.mTask.mTaskId));
+        }
+        try {
+            final int res = session.addToDisplay(window, window.mSeq, layoutParams,
+                    View.VISIBLE, token.getDisplayContent().getDisplayId(), tmpRect, tmpRect,
+                    tmpRect, null);
+            if (res < 0) {
+                Slog.w(TAG, "Failed to add snapshot starting window res=" + res);
+                return null;
+            }
+        } catch (RemoteException e) {
+            // Local call.
+        }
+        final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
+                surface);
+        window.setOuter(snapshotSurface);
+        try {
+            session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
+                    tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpRect, tmpConfiguration,
+                    surface);
+        } catch (RemoteException e) {
+            // Local call.
+        }
+        snapshotSurface.drawSnapshot(snapshot);
+        return snapshotSurface;
+    }
+
+    private TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface) {
+        mService = service;
+        mSession = WindowManagerGlobal.getWindowSession();
+        mWindow = window;
+        mSurface = surface;
+    }
+
+    @Override
+    public void remove() {
+        try {
+            mSession.remove(mWindow);
+        } catch (RemoteException e) {
+            // Local call.
+        }
+    }
+
+    private void drawSnapshot(GraphicBuffer snapshot) {
+
+        // TODO: Just wrap the buffer here without any copying.
+        final Canvas c = mSurface.lockHardwareCanvas();
+        c.drawBitmap(Bitmap.createHardwareBitmap(snapshot), 0, 0, null);
+        mSurface.unlockCanvasAndPost(c);
+        final boolean reportNextDraw;
+        synchronized (mService.mWindowMap) {
+            mHasDrawn = true;
+            reportNextDraw = mReportNextDraw;
+        }
+        if (reportNextDraw) {
+            reportDrawn();
+        }
+    }
+
+    private void reportDrawn() {
+        synchronized (mService.mWindowMap) {
+            mReportNextDraw = false;
+        }
+        try {
+            mSession.finishDrawing(mWindow);
+        } catch (RemoteException e) {
+            // Local call.
+        }
+    }
+
+    private static Handler sHandler = new Handler() {
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_REPORT_DRAW:
+                    final boolean hasDrawn;
+                    final TaskSnapshotSurface surface = (TaskSnapshotSurface) msg.obj;
+                    synchronized (surface.mService.mWindowMap) {
+                        hasDrawn = surface.mHasDrawn;
+                        if (!hasDrawn) {
+                            surface.mReportNextDraw = true;
+                        }
+                    }
+                    if (hasDrawn) {
+                        surface.reportDrawn();
+                    }
+                    break;
+            }
+        }
+    };
+
+    private static class Window extends BaseIWindow {
+
+        private TaskSnapshotSurface mOuter;
+
+        public void setOuter(TaskSnapshotSurface outer) {
+            mOuter = outer;
+        }
+
+        @Override
+        public void resized(Rect frame, Rect overscanInsets, Rect contentInsets, Rect visibleInsets,
+                Rect stableInsets, Rect outsets, boolean reportDraw, Configuration newConfig,
+                Rect backDropFrame, boolean forceLayout, boolean alwaysConsumeNavBar) {
+            if (reportDraw) {
+                sHandler.obtainMessage(MSG_REPORT_DRAW, mOuter).sendToTarget();
+            }
+        }
+    }
+}
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 9c303f8..1d598e7 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -17,6 +17,7 @@
 package com.android.server.wm;
 
 import android.content.res.Configuration;
+import android.graphics.GraphicBuffer;
 import android.graphics.Rect;
 import android.util.EventLog;
 import android.util.Slog;
@@ -238,6 +239,19 @@
         }
     }
 
+    /**
+     * @return a graphic buffer representing a screenshot of a task
+     */
+    public GraphicBuffer getSnapshot() {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                Slog.w(TAG_WM, "getSnapshot: taskId " + mTaskId + " not found.");
+                return null;
+            }
+            return mService.mTaskSnapshotController.getSnapshot(mContainer);
+        }
+    }
+
     @Override
     public String toString() {
         return "{TaskWindowContainerController taskId=" + mTaskId + "}";
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index 048affb..3639fb0 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -39,6 +39,7 @@
 import android.content.res.Configuration;
 import android.database.ContentObserver;
 import android.graphics.Bitmap;
+import android.graphics.GraphicBuffer;
 import android.graphics.PixelFormat;
 import android.graphics.Matrix;
 import android.graphics.Point;
@@ -183,6 +184,7 @@
 import static android.view.WindowManager.LayoutParams.LAST_APPLICATION_WINDOW;
 import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
 import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TASK_SNAPSHOT;
 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
 import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
@@ -580,6 +582,7 @@
 
     final UnknownAppVisibilityController mUnknownAppVisibilityController =
             new UnknownAppVisibilityController(this);
+    final TaskSnapshotController mTaskSnapshotController = new TaskSnapshotController(this);
 
     boolean mIsTouchDevice;
 
@@ -1215,7 +1218,9 @@
                           + token + ".  Aborting.");
                     return WindowManagerGlobal.ADD_APP_EXITING;
                 }
-                if (rootType == TYPE_APPLICATION_STARTING && atoken.firstWindowDrawn) {
+                if (rootType == TYPE_APPLICATION_STARTING
+                        && (attrs.privateFlags & PRIVATE_FLAG_TASK_SNAPSHOT) == 0
+                        && atoken.firstWindowDrawn) {
                     // No need for this guy!
                     if (DEBUG_STARTING_WINDOW || localLOGV) Slog.v(
                             TAG_WM, "**** NO NEED TO START: " + attrs.getTitle());
@@ -1966,7 +1971,8 @@
                         + " newVis=" + viewVisibility, stack);
             }
             if (viewVisibility == View.VISIBLE &&
-                    (win.mAppToken == null || !win.mAppToken.clientHidden)) {
+                    (win.mAppToken == null || win.mAttrs.type == TYPE_APPLICATION_STARTING
+                            || !win.mAppToken.clientHidden)) {
                 result = relayoutVisibleWindow(outConfig, result, win, winAnimator, attrChanges,
                         oldVisibility);
                 try {
@@ -2073,7 +2079,10 @@
             win.setDisplayLayoutNeeded();
             win.mGivenInsetsPending = (flags&WindowManagerGlobal.RELAYOUT_INSETS_PENDING) != 0;
             configChanged = updateOrientationFromAppTokensLocked(false, displayId);
-            mWindowPlacerLocked.performSurfacePlacement();
+
+            // We may be deferring layout passes at the moment, but since the client is interested
+            // in the new out values right now we need to force a layout.
+            mWindowPlacerLocked.performSurfacePlacement(true /* force */);
             if (toBeDisplayed && win.mIsWallpaper) {
                 DisplayInfo displayInfo = win.getDisplayContent().getDisplayInfo();
                 dc.mWallpaperController.updateWallpaperOffset(
@@ -3858,7 +3867,8 @@
             Trace.traceBegin(Trace.TRACE_TAG_WINDOW_MANAGER, "screenshotWallpaper");
             return screenshotApplications(null /* appToken */, DEFAULT_DISPLAY, -1 /* width */,
                     -1 /* height */, true /* includeFullDisplay */, 1f /* frameScale */,
-                    Bitmap.Config.ARGB_8888, true /* wallpaperOnly */);
+                    Bitmap.Config.ARGB_8888, true /* wallpaperOnly */, false /* includeDecor */,
+                    true /* toAshmem */);
         } finally {
             Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
         }
@@ -3879,7 +3889,8 @@
         FgThread.getHandler().post(() -> {
             Bitmap bm = screenshotApplications(null /* appToken */, DEFAULT_DISPLAY,
                     -1 /* width */, -1 /* height */, true /* includeFullDisplay */,
-                    1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */);
+                    1f /* frameScale */, Bitmap.Config.ARGB_8888, false /* wallpaperOnly */,
+                    false /* includeDecor */, true /* toAshmem */);
             try {
                 receiver.send(bm);
             } catch (RemoteException e) {
@@ -3900,10 +3911,14 @@
      * @param frameScale the scale to apply to the frame, only used when width = -1 and height = -1
      * @param config of the output bitmap
      * @param wallpaperOnly true if only the wallpaper layer should be included in the screenshot
+     * @param includeDecor whether to include window decors, like the status or navigation bar
+     *                     background of the window
+     * @param toAshmem whether to convert the resulting bitmap to ashmem; this should be set to
+     *                 true if the Bitmap is sent over binder, and false otherwise
      */
     private Bitmap screenshotApplications(IBinder appToken, int displayId, int width,
             int height, boolean includeFullDisplay, float frameScale, Bitmap.Config config,
-            boolean wallpaperOnly) {
+            boolean wallpaperOnly, boolean includeDecor, boolean toAshmem) {
         final DisplayContent displayContent;
         synchronized(mWindowMap) {
             displayContent = mRoot.getDisplayContentOrCreate(displayId);
@@ -3914,7 +3929,7 @@
             }
         }
         return displayContent.screenshotApplications(appToken, width, height,
-                includeFullDisplay, frameScale, config, wallpaperOnly);
+                includeFullDisplay, frameScale, config, wallpaperOnly, includeDecor, toAshmem);
     }
 
     /**
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 3daad43..b0945ee 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -16,7 +16,84 @@
 
 package com.android.server.wm;
 
-import android.app.ActivityManager;
+import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
+import static android.app.ActivityManager.StackId;
+import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
+import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
+import static android.app.ActivityManager.isLowRamDeviceStatic;
+import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
+import static android.view.Display.DEFAULT_DISPLAY;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
+import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
+import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
+import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
+import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
+import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
+import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
+import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
+import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
+import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
+import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
+import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
+import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
+import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
+import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
+import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
+import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
+import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
+import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
+import static android.view.WindowManagerPolicy.TRANSIT_ENTER;
+import static android.view.WindowManagerPolicy.TRANSIT_EXIT;
+import static android.view.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
+import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
+import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
+import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
+import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
+import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
+import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
+import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
+import static com.android.server.wm.WindowManagerService.localLOGV;
+import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
+import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
+import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
+import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
+
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.res.Configuration;
@@ -62,82 +139,6 @@
 import java.util.LinkedList;
 import java.util.function.Predicate;
 
-import static android.app.ActivityManager.StackId;
-import static android.app.ActivityManager.StackId.DOCKED_STACK_ID;
-import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
-import static android.os.Trace.TRACE_TAG_WINDOW_MANAGER;
-import static android.view.Display.DEFAULT_DISPLAY;
-import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
-import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
-import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
-import static android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_VISIBLE;
-import static android.view.WindowManager.LayoutParams.FIRST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.FIRST_SYSTEM_WINDOW;
-import static android.view.WindowManager.LayoutParams.FLAG_ALLOW_LOCK_WHILE_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM;
-import static android.view.WindowManager.LayoutParams.FLAG_DIM_BEHIND;
-import static android.view.WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD;
-import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
-import static android.view.WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;
-import static android.view.WindowManager.LayoutParams.FLAG_SCALED;
-import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
-import static android.view.WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED;
-import static android.view.WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
-import static android.view.WindowManager.LayoutParams.LAST_SUB_WINDOW;
-import static android.view.WindowManager.LayoutParams.MATCH_PARENT;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_COMPATIBLE_WINDOW;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_LAYOUT_CHILD_WINDOW_IN_PARENT_FRAME;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_NO_MOVE_ANIMATION;
-import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_WILL_NOT_REPLACE_ON_RELAUNCH;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
-import static android.view.WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_STARTING;
-import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
-import static android.view.WindowManager.LayoutParams.TYPE_DOCK_DIVIDER;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD;
-import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG;
-import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER;
-import static android.view.WindowManagerPolicy.FINISH_LAYOUT_REDO_WALLPAPER;
-import static android.view.WindowManagerPolicy.TRANSIT_ENTER;
-import static android.view.WindowManagerPolicy.TRANSIT_EXIT;
-import static android.view.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_DOCKED_DIVIDER;
-import static com.android.server.wm.DragResizeMode.DRAG_RESIZE_MODE_FREEFORM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ADD_REMOVE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ANIM;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_APP_TRANSITIONS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_CONFIGURATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_FOCUS_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_INPUT_METHOD;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYERS;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_ORIENTATION;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_POWER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_RESIZE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_STARTING_WINDOW;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_SURFACE_TRACE;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_VISIBILITY;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER;
-import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_WALLPAPER_LIGHT;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
-import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
-import static com.android.server.wm.WindowManagerService.H.SEND_NEW_CONFIGURATION;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_MULTIPLIER;
-import static com.android.server.wm.WindowManagerService.TYPE_LAYER_OFFSET;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_NORMAL;
-import static com.android.server.wm.WindowManagerService.UPDATE_FOCUS_WILL_PLACE_SURFACES;
-import static com.android.server.wm.WindowManagerService.WINDOWS_FREEZING_SCREENS_TIMEOUT;
-import static com.android.server.wm.WindowManagerService.localLOGV;
-import static com.android.server.wm.WindowStateAnimator.COMMIT_DRAW_PENDING;
-import static com.android.server.wm.WindowStateAnimator.DRAW_PENDING;
-import static com.android.server.wm.WindowStateAnimator.HAS_DRAWN;
-import static com.android.server.wm.WindowStateAnimator.READY_TO_SHOW;
-
 /** A window in the window manager. */
 class WindowState extends WindowContainer<WindowState> implements WindowManagerPolicy.WindowState {
     static final String TAG = TAG_WITH_CLASS_NAME ? "WindowState" : TAG_WM;
@@ -152,7 +153,8 @@
     // to capture touch events in that area.
     static final int RESIZE_HANDLE_WIDTH_IN_DP = 30;
 
-    private static final boolean DEBUG_DISABLE_SAVING_SURFACES = false;
+    private static final boolean DEBUG_DISABLE_SAVING_SURFACES = false ||
+            ENABLE_TASK_SNAPSHOTS;
 
     final WindowManagerService mService;
     final WindowManagerPolicy mPolicy;
@@ -2652,7 +2654,7 @@
             return false;
         }
 
-        if (ActivityManager.isLowRamDeviceStatic()) {
+        if (isLowRamDeviceStatic()) {
             // Don't save surfaces on Svelte devices.
             return false;
         }
diff --git a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
index 4df1001..897d5b86 100644
--- a/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
+++ b/services/core/java/com/android/server/wm/WindowSurfacePlacer.java
@@ -117,7 +117,11 @@
     }
 
     final void performSurfacePlacement() {
-        if (mDeferDepth > 0) {
+        performSurfacePlacement(false /* force */);
+    }
+
+    final void performSurfacePlacement(boolean force) {
+        if (mDeferDepth > 0 && !force) {
             return;
         }
         int loopCount = 6;
@@ -343,6 +347,8 @@
         mService.mAppTransition.postAnimationCallback();
         mService.mAppTransition.clear();
 
+        mService.mTaskSnapshotController.onTransitionStarting();
+
         mService.mOpeningApps.clear();
         mService.mClosingApps.clear();
         mService.mUnknownAppVisibilityController.clear();
@@ -513,17 +519,14 @@
                         + wtoken.startingMoved + " isRelaunching()="
                         + wtoken.isRelaunching());
 
-                if (wtoken.isRelaunching()) {
-                    return false;
-                }
-
                 final boolean drawnBeforeRestoring = wtoken.allDrawn;
                 wtoken.restoreSavedSurfaceForInterestingWindows();
 
-                if (!wtoken.allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
+                final boolean allDrawn = wtoken.allDrawn && !wtoken.isRelaunching();
+                if (!allDrawn && !wtoken.startingDisplayed && !wtoken.startingMoved) {
                     return false;
                 }
-                if (wtoken.allDrawn) {
+                if (allDrawn) {
                     reason = drawnBeforeRestoring ? APP_TRANSITION_WINDOWS_DRAWN
                             : APP_TRANSITION_SAVED_SURFACE;
                 } else {
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
new file mode 100644
index 0000000..5dff997
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.server.wm;
+
+import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
+import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static junit.framework.Assert.assertEquals;
+
+import android.platform.test.annotations.Presubmit;
+import android.support.test.filters.SmallTest;
+import android.support.test.runner.AndroidJUnit4;
+import android.util.ArraySet;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Test class for {@link TaskSnapshotController}.
+ *
+ * runtest frameworks-services -c com.android.server.wm.TaskSnapshotControllerTest
+ */
+@SmallTest
+@Presubmit
+@RunWith(AndroidJUnit4.class)
+public class TaskSnapshotControllerTest extends WindowTestsBase {
+
+    @Test
+    public void testGetClosingApps_closing() throws Exception {
+        final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
+                "closingWindow");
+        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+                true /* performLayout */, false /* isVoiceInteraction */);
+        final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
+        closingApps.add(closingWindow.mAppToken);
+        final ArraySet<Task> closingTasks = new ArraySet<>();
+        sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        assertEquals(1, closingTasks.size());
+        assertEquals(closingWindow.mAppToken.mTask, closingTasks.valueAt(0));
+    }
+
+    @Test
+    public void testGetClosingApps_notClosing() throws Exception {
+        final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW,
+                "closingWindow");
+        final WindowState openingWindow = createAppWindow(closingWindow.getTask(),
+                FIRST_APPLICATION_WINDOW, "openingWindow");
+        closingWindow.mAppToken.setVisibility(null, false /* visible */, TRANSIT_UNSET,
+                true /* performLayout */, false /* isVoiceInteraction */);
+        openingWindow.mAppToken.setVisibility(null, true /* visible */, TRANSIT_UNSET,
+                true /* performLayout */, false /* isVoiceInteraction */);
+        final ArraySet<AppWindowToken> closingApps = new ArraySet<>();
+        closingApps.add(closingWindow.mAppToken);
+        final ArraySet<Task> closingTasks = new ArraySet<>();
+        sWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks);
+        assertEquals(0, closingTasks.size());
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
index c4fd722..12e7a15 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TestWindowManagerPolicy.java
@@ -313,11 +313,6 @@
     }
 
     @Override
-    public void removeSplashScreen(IBinder appToken, StartingSurface surface) {
-
-    }
-
-    @Override
     public int prepareAddWindowLw(WindowState win,
             WindowManager.LayoutParams attrs) {
         return 0;
diff --git a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
index be080f5..466bd67 100644
--- a/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
+++ b/services/tests/servicestests/src/com/android/server/wm/WindowTestsBase.java
@@ -119,6 +119,12 @@
                 : createWindow(parent, type, parent.mToken, name);
     }
 
+    WindowState createAppWindow(Task task, int type, String name) {
+        final AppWindowToken token = new TestAppWindowToken(sDisplayContent);
+        task.addChild(token, 0);
+        return createWindow(null, type, token, name);
+    }
+
     WindowState createWindow(WindowState parent, int type, DisplayContent dc, String name) {
         final WindowToken token = createWindowToken(dc, type);
         return createWindow(parent, type, token, name);
diff --git a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
index 5e5ebd7..33f14767 100644
--- a/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
+++ b/tools/layoutlib/bridge/src/android/view/IWindowManagerImpl.java
@@ -16,6 +16,7 @@
 
 package android.view;
 
+import android.graphics.GraphicBuffer;
 import android.graphics.Point;
 import android.graphics.Rect;
 import com.android.internal.app.IAssistScreenshotReceiver;