When snapshots are disabled, fill it with single color.

Test: Launch DisableScreenshotsActivity, go to recents, make sure
content is blue. Reopen activity from home, make sure starting
window is blue.

Bug: 31339431
Change-Id: I29689774c3cdcb784d8f5bfa4f947a6f35b91e01
diff --git a/services/core/java/com/android/server/wm/TaskSnapshotController.java b/services/core/java/com/android/server/wm/TaskSnapshotController.java
index 4ae6dbe..b8d0b8c 100644
--- a/services/core/java/com/android/server/wm/TaskSnapshotController.java
+++ b/services/core/java/com/android/server/wm/TaskSnapshotController.java
@@ -17,11 +17,16 @@
 package com.android.server.wm;
 
 import static android.app.ActivityManager.ENABLE_TASK_SNAPSHOTS;
+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_RARELY;
+import static android.graphics.PixelFormat.RGBA_8888;
 
 import android.annotation.Nullable;
 import android.app.ActivityManager;
 import android.app.ActivityManager.StackId;
 import android.app.ActivityManager.TaskSnapshot;
+import android.graphics.Canvas;
 import android.graphics.GraphicBuffer;
 import android.os.Environment;
 import android.util.ArraySet;
@@ -48,6 +53,26 @@
  */
 class TaskSnapshotController {
 
+    /**
+     * Return value for {@link #getSnapshotMode}: We are allowed to take a real screenshot to be
+     * used as the snapshot.
+     */
+    @VisibleForTesting
+    static final int SNAPSHOT_MODE_REAL = 0;
+
+    /**
+     * Return value for {@link #getSnapshotMode}: We are not allowed to take a real screenshot but
+     * we should try to use the app theme to create a dummy representation of the app.
+     */
+    @VisibleForTesting
+    static final int SNAPSHOT_MODE_APP_THEME = 1;
+
+    /**
+     * Return value for {@link #getSnapshotMode}: We aren't allowed to take any snapshot.
+     */
+    @VisibleForTesting
+    static final int SNAPSHOT_MODE_NONE = 2;
+
     private final WindowManagerService mService;
 
     private final TaskSnapshotCache mCache;
@@ -88,10 +113,21 @@
         getClosingTasks(closingApps, mTmpTasks);
         for (int i = mTmpTasks.size() - 1; i >= 0; i--) {
             final Task task = mTmpTasks.valueAt(i);
-            if (!canSnapshotTask(task)) {
-                continue;
+            final int mode = getSnapshotMode(task);
+            final TaskSnapshot snapshot;
+            switch (mode) {
+                case SNAPSHOT_MODE_NONE:
+                    continue;
+                case SNAPSHOT_MODE_APP_THEME:
+                    snapshot = drawAppThemeSnapshot(task);
+                    break;
+                case SNAPSHOT_MODE_REAL:
+                    snapshot = snapshotTask(task);
+                    break;
+                default:
+                    snapshot = null;
+                    break;
             }
-            final TaskSnapshot snapshot = snapshotTask(task);
             if (snapshot != null) {
                 mCache.putSnapshot(task, snapshot);
                 mPersister.persistSnapshot(task.mTaskId, task.mUserId, snapshot);
@@ -153,12 +189,42 @@
     }
 
     @VisibleForTesting
-    boolean canSnapshotTask(Task task) {
-        // TODO: Figure out what happens when snapshots are disabled. Can we draw a splash screen
-        // instead?
+    int getSnapshotMode(Task task) {
         final AppWindowToken topChild = task.getTopChild();
-        return !StackId.isHomeOrRecentsStack(task.mStack.mStackId)
-                && topChild != null && !topChild.shouldDisablePreviewScreenshots();
+        if (StackId.isHomeOrRecentsStack(task.mStack.mStackId)) {
+            return SNAPSHOT_MODE_NONE;
+        } else if (topChild != null && topChild.shouldDisablePreviewScreenshots()) {
+            return SNAPSHOT_MODE_APP_THEME;
+        } else {
+            return SNAPSHOT_MODE_REAL;
+        }
+    }
+
+    /**
+     * If we are not allowed to take a real screenshot, this attempts to represent the app as best
+     * as possible by using the theme's window background.
+     */
+    private TaskSnapshot drawAppThemeSnapshot(Task task) {
+        final AppWindowToken topChild = task.getTopChild();
+        if (topChild == null) {
+            return null;
+        }
+        final WindowState mainWindow = topChild.findMainWindow();
+        if (mainWindow == null) {
+            return null;
+        }
+        final int color = task.getTaskDescription().getBackgroundColor();
+        final GraphicBuffer buffer = GraphicBuffer.create(mainWindow.getFrameLw().width(),
+                mainWindow.getFrameLw().height(),
+                RGBA_8888, USAGE_HW_TEXTURE | USAGE_SW_WRITE_RARELY | USAGE_SW_READ_NEVER);
+        if (buffer == null) {
+            return null;
+        }
+        final Canvas c = buffer.lockCanvas();
+        c.drawColor(color);
+        buffer.unlockCanvasAndPost(c);
+        return new TaskSnapshot(buffer, topChild.getConfiguration().orientation,
+                mainWindow.mStableInsets, false /* reduced */, 1.0f /* scale */);
     }
 
     /**
diff --git a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
index 45a7999..2752340 100644
--- a/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
+++ b/services/tests/servicestests/src/com/android/server/wm/TaskSnapshotControllerTest.java
@@ -18,6 +18,7 @@
 
 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW;
 import static com.android.server.wm.AppTransition.TRANSIT_UNSET;
+import static com.android.server.wm.TaskSnapshotController.*;
 import static junit.framework.Assert.assertEquals;
 import static junit.framework.Assert.assertFalse;
 import static junit.framework.Assert.assertTrue;
@@ -72,13 +73,15 @@
     }
 
     @Test
-    public void testSnapshotsDisabled() throws Exception {
+    public void testGetSnapshotMode() throws Exception {
         final WindowState disabledWindow = createWindow(null,
                 FIRST_APPLICATION_WINDOW, sDisplayContent, "disabledWindow");
         disabledWindow.mAppToken.setDisablePreviewSnapshots(true);
-        assertFalse(sWm.mTaskSnapshotController.canSnapshotTask(disabledWindow.getTask()));
+        assertEquals(SNAPSHOT_MODE_APP_THEME,
+                sWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask()));
         final WindowState normalWindow = createWindow(null,
                 FIRST_APPLICATION_WINDOW, sDisplayContent, "normalWindow");
-        assertTrue(sWm.mTaskSnapshotController.canSnapshotTask(normalWindow.getTask()));
+        assertEquals(SNAPSHOT_MODE_REAL,
+                sWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask()));
     }
 }
diff --git a/tests/ActivityTests/AndroidManifest.xml b/tests/ActivityTests/AndroidManifest.xml
index 4dd51dc..64cdcf7 100644
--- a/tests/ActivityTests/AndroidManifest.xml
+++ b/tests/ActivityTests/AndroidManifest.xml
@@ -80,7 +80,8 @@
         <receiver android:name="TrackTimeReceiver" />
         <receiver android:name="AlarmSpamReceiver" />
         <activity android:name="DisableScreenshotsActivity"
-                android:label="DisableScreenshots">
+                android:label="DisableScreenshots"
+                android:theme="@style/DisableScreenshots">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.LAUNCHER" />
diff --git a/tests/ActivityTests/res/values/colors.xml b/tests/ActivityTests/res/values/colors.xml
new file mode 100644
index 0000000..9925722
--- /dev/null
+++ b/tests/ActivityTests/res/values/colors.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<!-- Copyright (C) 2017 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.
+-->
+<resources>
+    <color name="blue">#0000ff</color>
+</resources>
\ No newline at end of file
diff --git a/tests/ActivityTests/res/values/themes.xml b/tests/ActivityTests/res/values/themes.xml
index 67f5938..b8dd830 100644
--- a/tests/ActivityTests/res/values/themes.xml
+++ b/tests/ActivityTests/res/values/themes.xml
@@ -22,4 +22,8 @@
         <item name="android:windowEnterAnimation">@anim/slow_enter</item>
         <item name="android:windowExitAnimation">@anim/slow_exit</item>
     </style>
+    <style name="DisableScreenshots" parent="@android:style/Theme.Material">
+        <item name="android:colorBackground">@color/blue</item>
+        <item name="android:windowBackground">@color/blue</item>
+    </style>
 </resources>
diff --git a/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java b/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java
index de24ca2..fa5724e 100644
--- a/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java
+++ b/tests/ActivityTests/src/com/google/android/test/activity/DisableScreenshotsActivity.java
@@ -20,6 +20,7 @@
 import android.app.Activity;
 import android.graphics.Color;
 import android.os.Bundle;
+import android.os.SystemClock;
 
 /**
  * Activity for which screenshotting is disabled.
@@ -32,4 +33,13 @@
         setDisablePreviewScreenshots(true);
         getWindow().getDecorView().setBackgroundColor(Color.RED);
     }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+
+        // This is to simulate slowness over resuming the app, such that we have plenty of time to
+        // see the starting window.
+        SystemClock.sleep(500);
+    }
 }