1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.wm; 18 19 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; 20 import static android.view.WindowManager.LayoutParams.FIRST_APPLICATION_WINDOW; 21 import static android.view.WindowManager.LayoutParams.FLAG_SECURE; 22 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn; 25 import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_APP_THEME; 26 import static com.android.server.wm.TaskSnapshotController.SNAPSHOT_MODE_REAL; 27 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertFalse; 30 import static org.junit.Assert.assertNotEquals; 31 import static org.junit.Assert.assertSame; 32 import static org.junit.Assert.assertTrue; 33 import static org.junit.Assert.fail; 34 import static org.mockito.ArgumentMatchers.eq; 35 import static org.mockito.Mockito.verify; 36 import static org.mockito.Mockito.when; 37 38 import android.app.WindowConfiguration; 39 import android.content.ComponentName; 40 import android.content.res.Configuration; 41 import android.graphics.ColorSpace; 42 import android.graphics.PixelFormat; 43 import android.graphics.Point; 44 import android.graphics.Rect; 45 import android.hardware.HardwareBuffer; 46 import android.platform.test.annotations.Presubmit; 47 import android.util.ArraySet; 48 import android.window.TaskSnapshot; 49 50 import androidx.test.filters.SmallTest; 51 52 import com.google.android.collect.Sets; 53 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.mockito.Mockito; 57 58 /** 59 * Test class for {@link TaskSnapshotController}. 60 * 61 * Build/Install/Run: 62 * * atest WmTests:TaskSnapshotControllerTest 63 */ 64 @SmallTest 65 @Presubmit 66 @RunWith(WindowTestRunner.class) 67 public class TaskSnapshotControllerTest extends WindowTestsBase { 68 69 @Test testGetClosingApps_closing()70 public void testGetClosingApps_closing() { 71 final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, 72 "closingWindow"); 73 closingWindow.mActivityRecord.commitVisibility( 74 false /* visible */, true /* performLayout */); 75 final ArraySet<ActivityRecord> closingApps = new ArraySet<>(); 76 closingApps.add(closingWindow.mActivityRecord); 77 final ArraySet<Task> closingTasks = new ArraySet<>(); 78 mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); 79 assertEquals(1, closingTasks.size()); 80 assertEquals(closingWindow.mActivityRecord.getTask(), closingTasks.valueAt(0)); 81 } 82 83 @Test testGetClosingApps_notClosing()84 public void testGetClosingApps_notClosing() { 85 final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, 86 "closingWindow"); 87 final WindowState openingWindow = createAppWindow(closingWindow.getTask(), 88 FIRST_APPLICATION_WINDOW, "openingWindow"); 89 closingWindow.mActivityRecord.commitVisibility( 90 false /* visible */, true /* performLayout */); 91 openingWindow.mActivityRecord.commitVisibility( 92 true /* visible */, true /* performLayout */); 93 final ArraySet<ActivityRecord> closingApps = new ArraySet<>(); 94 closingApps.add(closingWindow.mActivityRecord); 95 final ArraySet<Task> closingTasks = new ArraySet<>(); 96 mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); 97 assertEquals(0, closingTasks.size()); 98 } 99 100 @Test testGetClosingApps_skipClosingAppsSnapshotTasks()101 public void testGetClosingApps_skipClosingAppsSnapshotTasks() { 102 final WindowState closingWindow = createWindow(null, FIRST_APPLICATION_WINDOW, 103 "closingWindow"); 104 closingWindow.mActivityRecord.commitVisibility( 105 false /* visible */, true /* performLayout */); 106 final ArraySet<ActivityRecord> closingApps = new ArraySet<>(); 107 closingApps.add(closingWindow.mActivityRecord); 108 final ArraySet<Task> closingTasks = new ArraySet<>(); 109 mWm.mTaskSnapshotController.addSkipClosingAppSnapshotTasks( 110 Sets.newArraySet(closingWindow.mActivityRecord.getTask())); 111 mWm.mTaskSnapshotController.getClosingTasks(closingApps, closingTasks); 112 assertEquals(0, closingTasks.size()); 113 } 114 115 @Test testGetSnapshotMode()116 public void testGetSnapshotMode() { 117 final WindowState disabledWindow = createWindow(null, 118 FIRST_APPLICATION_WINDOW, mDisplayContent, "disabledWindow"); 119 disabledWindow.mActivityRecord.setRecentsScreenshotEnabled(false); 120 assertEquals(SNAPSHOT_MODE_APP_THEME, 121 mWm.mTaskSnapshotController.getSnapshotMode(disabledWindow.getTask())); 122 123 final WindowState normalWindow = createWindow(null, 124 FIRST_APPLICATION_WINDOW, mDisplayContent, "normalWindow"); 125 assertEquals(SNAPSHOT_MODE_REAL, 126 mWm.mTaskSnapshotController.getSnapshotMode(normalWindow.getTask())); 127 128 final WindowState secureWindow = createWindow(null, 129 FIRST_APPLICATION_WINDOW, mDisplayContent, "secureWindow"); 130 secureWindow.mAttrs.flags |= FLAG_SECURE; 131 assertEquals(SNAPSHOT_MODE_APP_THEME, 132 mWm.mTaskSnapshotController.getSnapshotMode(secureWindow.getTask())); 133 } 134 135 @Test testSnapshotBuilder()136 public void testSnapshotBuilder() { 137 final HardwareBuffer buffer = Mockito.mock(HardwareBuffer.class); 138 final ColorSpace sRGB = ColorSpace.get(ColorSpace.Named.SRGB); 139 final long id = 1234L; 140 final ComponentName activityComponent = new ComponentName("package", ".Class"); 141 final int windowingMode = WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 142 final int appearance = APPEARANCE_LIGHT_STATUS_BARS; 143 final int pixelFormat = PixelFormat.RGBA_8888; 144 final int orientation = Configuration.ORIENTATION_PORTRAIT; 145 final float scaleFraction = 0.25f; 146 final Rect contentInsets = new Rect(1, 2, 3, 4); 147 final Rect letterboxInsets = new Rect(5, 6, 7, 8); 148 final Point taskSize = new Point(9, 10); 149 150 try { 151 TaskSnapshot.Builder builder = 152 new TaskSnapshot.Builder(); 153 builder.setId(id); 154 builder.setTopActivityComponent(activityComponent); 155 builder.setAppearance(appearance); 156 builder.setWindowingMode(windowingMode); 157 builder.setColorSpace(sRGB); 158 builder.setOrientation(orientation); 159 builder.setContentInsets(contentInsets); 160 builder.setLetterboxInsets(letterboxInsets); 161 builder.setIsTranslucent(true); 162 builder.setSnapshot(buffer); 163 builder.setIsRealSnapshot(true); 164 builder.setPixelFormat(pixelFormat); 165 builder.setTaskSize(taskSize); 166 167 // Not part of TaskSnapshot itself, used in screenshot process 168 assertEquals(pixelFormat, builder.getPixelFormat()); 169 170 TaskSnapshot snapshot = builder.build(); 171 assertEquals(id, snapshot.getId()); 172 assertEquals(activityComponent, snapshot.getTopActivityComponent()); 173 assertEquals(appearance, snapshot.getAppearance()); 174 assertEquals(windowingMode, snapshot.getWindowingMode()); 175 assertEquals(sRGB, snapshot.getColorSpace()); 176 // Snapshots created with the Builder class are always high-res. The only way to get a 177 // low-res snapshot is to load it from the disk in TaskSnapshotLoader. 178 assertFalse(snapshot.isLowResolution()); 179 assertEquals(orientation, snapshot.getOrientation()); 180 assertEquals(contentInsets, snapshot.getContentInsets()); 181 assertEquals(letterboxInsets, snapshot.getLetterboxInsets()); 182 assertTrue(snapshot.isTranslucent()); 183 assertSame(buffer, snapshot.getHardwareBuffer()); 184 assertTrue(snapshot.isRealSnapshot()); 185 assertEquals(taskSize, snapshot.getTaskSize()); 186 } finally { 187 if (buffer != null) { 188 buffer.close(); 189 } 190 } 191 } 192 193 @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD}) 194 @Test testCreateTaskSnapshotWithExcludingIme()195 public void testCreateTaskSnapshotWithExcludingIme() { 196 Task task = mAppWindow.mActivityRecord.getTask(); 197 spyOn(task); 198 spyOn(mDisplayContent); 199 when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(false); 200 // Intentionally set the SurfaceControl of input method window as null. 201 mDisplayContent.mInputMethodWindow.setSurfaceControl(null); 202 // Verify no NPE happens when calling createTaskSnapshot. 203 try { 204 final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); 205 mWm.mTaskSnapshotController.createTaskSnapshot(mAppWindow.mActivityRecord.getTask(), 206 1f /* scaleFraction */, PixelFormat.UNKNOWN, null /* outTaskSize */, builder); 207 } catch (NullPointerException e) { 208 fail("There should be no exception when calling createTaskSnapshot"); 209 } 210 } 211 212 @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD}) 213 @Test testCreateTaskSnapshotWithIncludingIme()214 public void testCreateTaskSnapshotWithIncludingIme() { 215 Task task = mAppWindow.mActivityRecord.getTask(); 216 spyOn(task); 217 spyOn(mDisplayContent); 218 spyOn(mDisplayContent.mInputMethodWindow); 219 when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(true); 220 // Intentionally set the IME window is in visible state. 221 doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); 222 // Verify no NPE happens when calling createTaskSnapshot. 223 try { 224 final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); 225 spyOn(builder); 226 mWm.mTaskSnapshotController.createTaskSnapshot( 227 mAppWindow.mActivityRecord.getTask(), 1f /* scaleFraction */, 228 PixelFormat.UNKNOWN, null /* outTaskSize */, builder); 229 // Verify the builder should includes IME surface. 230 verify(builder).setHasImeSurface(eq(true)); 231 builder.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)); 232 builder.setTaskSize(new Point(100, 100)); 233 final TaskSnapshot snapshot = builder.build(); 234 assertTrue(snapshot.hasImeSurface()); 235 } catch (NullPointerException e) { 236 fail("There should be no exception when calling createTaskSnapshot"); 237 } 238 } 239 240 @UseTestDisplay(addWindows = W_ACTIVITY) 241 @Test testPrepareTaskSnapshot()242 public void testPrepareTaskSnapshot() { 243 mAppWindow.mWinAnimator.mLastAlpha = 1f; 244 spyOn(mAppWindow.mWinAnimator); 245 doReturn(true).when(mAppWindow.mWinAnimator).getShown(); 246 doReturn(true).when(mAppWindow.mActivityRecord).isSurfaceShowing(); 247 248 final TaskSnapshot.Builder builder = 249 new TaskSnapshot.Builder(); 250 boolean success = mWm.mTaskSnapshotController.prepareTaskSnapshot( 251 mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); 252 253 assertTrue(success); 254 // The pixel format should be selected automatically. 255 assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat()); 256 257 // Snapshot should not be taken while the rotation of activity and task are different. 258 doReturn(true).when(mAppWindow.mActivityRecord).hasFixedRotationTransform(); 259 success = mWm.mTaskSnapshotController.prepareTaskSnapshot( 260 mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); 261 262 assertFalse(success); 263 } 264 } 265