Fill task snapshot with background color

Make sure to fill the portions that are not covered by the
snapshot are filled with the task background color.

Also fix an issue where the starting window was removed across
configuration changes.

Test: runtest frameworks-services -c
com.android.server.wm.TaskSnapshotSurfaceTest
Bug: 31339431
Change-Id: I2451be87aff79b337015ab4bba72cfa03c0d3582
diff --git a/services/core/java/com/android/server/am/TaskRecord.java b/services/core/java/com/android/server/am/TaskRecord.java
index 2745fe5..7b4d289 100644
--- a/services/core/java/com/android/server/am/TaskRecord.java
+++ b/services/core/java/com/android/server/am/TaskRecord.java
@@ -416,7 +416,7 @@
         final Configuration overrideConfig = getOverrideConfiguration();
         mWindowContainerController = new TaskWindowContainerController(taskId, this, getStackId(),
                 userId, bounds, overrideConfig, mResizeMode, isHomeTask(), isOnTopLauncher(), onTop,
-                showForAllUsers);
+                showForAllUsers, lastTaskDescription);
     }
 
     void removeWindowContainer() {
@@ -1402,6 +1402,9 @@
             }
             lastTaskDescription = new TaskDescription(label, null, iconFilename, colorPrimary,
                     colorBackground);
+            if (mWindowContainerController != null) {
+                mWindowContainerController.setTaskDescription(lastTaskDescription);
+            }
             // Update the task affiliation color if we are the parent of the group
             if (taskId == mAffiliatedTaskId) {
                 mAffiliatedTaskColor = lastTaskDescription.getPrimaryColor();
diff --git a/services/core/java/com/android/server/wm/AppWindowToken.java b/services/core/java/com/android/server/wm/AppWindowToken.java
index 29c685d..079dc8f 100644
--- a/services/core/java/com/android/server/wm/AppWindowToken.java
+++ b/services/core/java/com/android/server/wm/AppWindowToken.java
@@ -675,7 +675,7 @@
             // well there is no point now.
             if (DEBUG_STARTING_WINDOW) Slog.v(TAG_WM, "Nulling last startingData");
             startingData = null;
-        } else if (mChildren.size() == 1 && startingSurface != null) {
+        } else if (mChildren.size() == 1 && startingSurface != null && !isRelaunching()) {
             // If this is the last window except for a starting transition window,
             // we need to get rid of the starting transition.
             if (getController() != null) {
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index 680d0f2..3a3ec71 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -30,6 +30,7 @@
 import static com.android.server.wm.WindowManagerService.H.RESIZE_TASK;
 
 import android.app.ActivityManager.StackId;
+import android.app.ActivityManager.TaskDescription;
 import android.content.pm.ActivityInfo;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -90,9 +91,11 @@
     // Whether this task is an on-top launcher task, which is determined by the root activity.
     private boolean mIsOnTopLauncher;
 
+    private TaskDescription mTaskDescription;
+
     Task(int taskId, TaskStack stack, int userId, WindowManagerService service, Rect bounds,
             Configuration overrideConfig, boolean isOnTopLauncher, int resizeMode, boolean homeTask,
-            TaskWindowContainerController controller) {
+            TaskDescription taskDescription, TaskWindowContainerController controller) {
         mTaskId = taskId;
         mStack = stack;
         mUserId = userId;
@@ -102,6 +105,7 @@
         mHomeTask = homeTask;
         setController(controller);
         setBounds(bounds, overrideConfig);
+        mTaskDescription = taskDescription;
     }
 
     DisplayContent getDisplayContent() {
@@ -647,6 +651,14 @@
         }
     }
 
+    void setTaskDescription(TaskDescription taskDescription) {
+        mTaskDescription = taskDescription;
+    }
+
+    TaskDescription getTaskDescription() {
+        return mTaskDescription;
+    }
+
     @Override
     boolean fillsParent() {
         return mFillsParent || !StackId.isTaskResizeAllowed(mStack.mStackId);
@@ -688,6 +700,5 @@
             pw.println(triplePrefix + "Activity #" + i + " " + wtoken);
             wtoken.dump(pw, triplePrefix);
         }
-
     }
 }
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
index 4a09423..cfcbbd0 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotSurface.java
@@ -26,12 +26,16 @@
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
 import static com.android.server.wm.WindowManagerDebugConfig.TAG_WM;
 
+import android.app.ActivityManager.TaskDescription;
 import android.content.res.Configuration;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Color;
 import android.graphics.GraphicBuffer;
+import android.graphics.Paint;
 import android.graphics.Rect;
 import android.os.Handler;
+import android.os.Looper;
 import android.os.Message;
 import android.os.RemoteException;
 import android.util.Slog;
@@ -43,6 +47,7 @@
 import android.view.WindowManagerGlobal;
 import android.view.WindowManagerPolicy.StartingSurface;
 
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.view.BaseIWindow;
 
 /**
@@ -61,6 +66,7 @@
     private final WindowManagerService mService;
     private boolean mHasDrawn;
     private boolean mReportNextDraw;
+    private Paint mFillBackgroundPaint = new Paint();
 
     static TaskSnapshotSurface create(WindowManagerService service, AppWindowToken token,
             GraphicBuffer snapshot) {
@@ -73,6 +79,7 @@
         final Rect tmpRect = new Rect();
         final Rect tmpFrame = new Rect();
         final Configuration tmpConfiguration = new Configuration();
+        int fillBackgroundColor = Color.WHITE;
         synchronized (service.mWindowMap) {
             layoutParams.type = TYPE_APPLICATION_STARTING;
             layoutParams.format = snapshot.getFormat();
@@ -90,6 +97,12 @@
             layoutParams.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                     | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION;
             layoutParams.setTitle(String.format(TITLE_FORMAT, token.mTask.mTaskId));
+            if (token.mTask != null) {
+                final TaskDescription taskDescription = token.mTask.getTaskDescription();
+                if (taskDescription != null) {
+                    fillBackgroundColor = taskDescription.getBackgroundColor();
+                }
+            }
         }
         try {
             final int res = session.addToDisplay(window, window.mSeq, layoutParams,
@@ -103,7 +116,7 @@
             // Local call.
         }
         final TaskSnapshotSurface snapshotSurface = new TaskSnapshotSurface(service, window,
-                surface);
+                surface, fillBackgroundColor);
         window.setOuter(snapshotSurface);
         try {
             session.relayout(window, window.mSeq, layoutParams, -1, -1, View.VISIBLE, 0, tmpFrame,
@@ -116,11 +129,14 @@
         return snapshotSurface;
     }
 
-    private TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface) {
+    @VisibleForTesting
+    TaskSnapshotSurface(WindowManagerService service, Window window, Surface surface,
+            int fillBackgroundColor) {
         mService = service;
         mSession = WindowManagerGlobal.getWindowSession();
         mWindow = window;
         mSurface = surface;
+        mFillBackgroundPaint.setColor(fillBackgroundColor);
     }
 
     @Override
@@ -136,7 +152,9 @@
 
         // TODO: Just wrap the buffer here without any copying.
         final Canvas c = mSurface.lockHardwareCanvas();
-        c.drawBitmap(Bitmap.createHardwareBitmap(snapshot), 0, 0, null);
+        final Bitmap b = Bitmap.createHardwareBitmap(snapshot);
+        fillEmptyBackground(c, b);
+        c.drawBitmap(b, 0, 0, null);
         mSurface.unlockCanvasAndPost(c);
         final boolean reportNextDraw;
         synchronized (mService.mWindowMap) {
@@ -149,6 +167,21 @@
         mSurface.release();
     }
 
+    @VisibleForTesting
+    void fillEmptyBackground(Canvas c, Bitmap b) {
+        final boolean fillHorizontally = c.getWidth() > b.getWidth();
+        final boolean fillVertically = c.getHeight() > b.getHeight();
+        if (fillHorizontally) {
+            c.drawRect(b.getWidth(), 0, c.getWidth(), fillVertically
+                        ? b.getHeight()
+                        : c.getHeight(),
+                    mFillBackgroundPaint);
+        }
+        if (fillVertically) {
+            c.drawRect(0, b.getHeight(), c.getWidth(), c.getHeight(), mFillBackgroundPaint);
+        }
+    }
+
     private void reportDrawn() {
         synchronized (mService.mWindowMap) {
             mReportNextDraw = false;
@@ -160,7 +193,7 @@
         }
     }
 
-    private static Handler sHandler = new Handler() {
+    private static Handler sHandler = new Handler(Looper.getMainLooper()) {
 
         @Override
         public void handleMessage(Message msg) {
diff --git a/services/core/java/com/android/server/wm/TaskWindowContainerController.java b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
index 3c438ca..61a2cd9 100644
--- a/services/core/java/com/android/server/wm/TaskWindowContainerController.java
+++ b/services/core/java/com/android/server/wm/TaskWindowContainerController.java
@@ -16,6 +16,7 @@
 
 package com.android.server.wm;
 
+import android.app.ActivityManager.TaskDescription;
 import android.app.ActivityManager.TaskSnapshot;
 import android.content.res.Configuration;
 import android.graphics.Rect;
@@ -62,7 +63,8 @@
 
     public TaskWindowContainerController(int taskId, TaskWindowContainerListener listener,
             int stackId, int userId, Rect bounds, Configuration overrideConfig, int resizeMode,
-            boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers) {
+            boolean homeTask, boolean isOnTopLauncher, boolean toTop, boolean showForAllUsers,
+            TaskDescription taskDescription) {
         super(listener, WindowManagerService.getInstance());
         mTaskId = taskId;
 
@@ -79,7 +81,7 @@
             }
             EventLog.writeEvent(WM_TASK_CREATED, taskId, stackId);
             final Task task = createTask(taskId, stack, userId, bounds, overrideConfig, resizeMode,
-                    homeTask, isOnTopLauncher);
+                    homeTask, isOnTopLauncher, taskDescription);
             final int position = toTop ? POSITION_TOP : POSITION_BOTTOM;
             stack.addTask(task, position, showForAllUsers, true /* moveParents */);
         }
@@ -88,9 +90,9 @@
     @VisibleForTesting
     Task createTask(int taskId, TaskStack stack, int userId, Rect bounds,
             Configuration overrideConfig, int resizeMode, boolean homeTask,
-            boolean isOnTopLauncher) {
+            boolean isOnTopLauncher, TaskDescription taskDescription) {
         return new Task(taskId, stack, userId, mService, bounds, overrideConfig, isOnTopLauncher,
-                resizeMode, homeTask, this);
+                resizeMode, homeTask, taskDescription, this);
     }
 
     @Override
@@ -263,6 +265,16 @@
         }
     }
 
+    public void setTaskDescription(TaskDescription taskDescription) {
+        synchronized (mWindowMap) {
+            if (mContainer == null) {
+                Slog.w(TAG_WM, "setTaskDescription: taskId " + mTaskId + " not found.");
+                return;
+            }
+            mContainer.setTaskDescription(taskDescription);
+        }
+    }
+
     void reportSnapshotChanged(TaskSnapshot snapshot) {
         mHandler.obtainMessage(REPORT_SNAPSHOT_CHANGED, snapshot).sendToTarget();
     }