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.setDisablePreviewScreenshots(true); 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 Point taskSize = new Point(5, 6); 148 149 try { 150 TaskSnapshot.Builder builder = 151 new TaskSnapshot.Builder(); 152 builder.setId(id); 153 builder.setTopActivityComponent(activityComponent); 154 builder.setAppearance(appearance); 155 builder.setWindowingMode(windowingMode); 156 builder.setColorSpace(sRGB); 157 builder.setOrientation(orientation); 158 builder.setContentInsets(contentInsets); 159 builder.setIsTranslucent(true); 160 builder.setSnapshot(buffer); 161 builder.setIsRealSnapshot(true); 162 builder.setPixelFormat(pixelFormat); 163 builder.setTaskSize(taskSize); 164 165 // Not part of TaskSnapshot itself, used in screenshot process 166 assertEquals(pixelFormat, builder.getPixelFormat()); 167 168 TaskSnapshot snapshot = builder.build(); 169 assertEquals(id, snapshot.getId()); 170 assertEquals(activityComponent, snapshot.getTopActivityComponent()); 171 assertEquals(appearance, snapshot.getAppearance()); 172 assertEquals(windowingMode, snapshot.getWindowingMode()); 173 assertEquals(sRGB, snapshot.getColorSpace()); 174 // Snapshots created with the Builder class are always high-res. The only way to get a 175 // low-res snapshot is to load it from the disk in TaskSnapshotLoader. 176 assertFalse(snapshot.isLowResolution()); 177 assertEquals(orientation, snapshot.getOrientation()); 178 assertEquals(contentInsets, snapshot.getContentInsets()); 179 assertTrue(snapshot.isTranslucent()); 180 assertSame(buffer, snapshot.getHardwareBuffer()); 181 assertTrue(snapshot.isRealSnapshot()); 182 assertEquals(taskSize, snapshot.getTaskSize()); 183 } finally { 184 if (buffer != null) { 185 buffer.close(); 186 } 187 } 188 } 189 190 @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD}) 191 @Test testCreateTaskSnapshotWithExcludingIme()192 public void testCreateTaskSnapshotWithExcludingIme() { 193 Task task = mAppWindow.mActivityRecord.getTask(); 194 spyOn(task); 195 spyOn(mDisplayContent); 196 when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(false); 197 // Intentionally set the SurfaceControl of input method window as null. 198 mDisplayContent.mInputMethodWindow.setSurfaceControl(null); 199 // Verify no NPE happens when calling createTaskSnapshot. 200 try { 201 final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); 202 mWm.mTaskSnapshotController.createTaskSnapshot(mAppWindow.mActivityRecord.getTask(), 203 1f /* scaleFraction */, PixelFormat.UNKNOWN, null /* outTaskSize */, builder); 204 } catch (NullPointerException e) { 205 fail("There should be no exception when calling createTaskSnapshot"); 206 } 207 } 208 209 @UseTestDisplay(addWindows = {W_ACTIVITY, W_INPUT_METHOD}) 210 @Test testCreateTaskSnapshotWithIncludingIme()211 public void testCreateTaskSnapshotWithIncludingIme() { 212 Task task = mAppWindow.mActivityRecord.getTask(); 213 spyOn(task); 214 spyOn(mDisplayContent); 215 spyOn(mDisplayContent.mInputMethodWindow); 216 when(task.getDisplayContent().shouldImeAttachedToApp()).thenReturn(true); 217 // Intentionally set the IME window is in visible state. 218 doReturn(true).when(mDisplayContent.mInputMethodWindow).isVisible(); 219 // Verify no NPE happens when calling createTaskSnapshot. 220 try { 221 final TaskSnapshot.Builder builder = new TaskSnapshot.Builder(); 222 spyOn(builder); 223 mWm.mTaskSnapshotController.createTaskSnapshot( 224 mAppWindow.mActivityRecord.getTask(), 1f /* scaleFraction */, 225 PixelFormat.UNKNOWN, null /* outTaskSize */, builder); 226 // Verify the builder should includes IME surface. 227 verify(builder).setHasImeSurface(eq(true)); 228 builder.setColorSpace(ColorSpace.get(ColorSpace.Named.SRGB)); 229 builder.setTaskSize(new Point(100, 100)); 230 final TaskSnapshot snapshot = builder.build(); 231 assertTrue(snapshot.hasImeSurface()); 232 } catch (NullPointerException e) { 233 fail("There should be no exception when calling createTaskSnapshot"); 234 } 235 } 236 237 @UseTestDisplay(addWindows = W_ACTIVITY) 238 @Test testPrepareTaskSnapshot()239 public void testPrepareTaskSnapshot() { 240 mAppWindow.mWinAnimator.mLastAlpha = 1f; 241 spyOn(mAppWindow.mWinAnimator); 242 doReturn(true).when(mAppWindow.mWinAnimator).getShown(); 243 doReturn(true).when(mAppWindow.mActivityRecord).isSurfaceShowing(); 244 245 final TaskSnapshot.Builder builder = 246 new TaskSnapshot.Builder(); 247 boolean success = mWm.mTaskSnapshotController.prepareTaskSnapshot( 248 mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); 249 250 assertTrue(success); 251 // The pixel format should be selected automatically. 252 assertNotEquals(PixelFormat.UNKNOWN, builder.getPixelFormat()); 253 254 // Snapshot should not be taken while the rotation of activity and task are different. 255 doReturn(true).when(mAppWindow.mActivityRecord).hasFixedRotationTransform(); 256 success = mWm.mTaskSnapshotController.prepareTaskSnapshot( 257 mAppWindow.mActivityRecord.getTask(), PixelFormat.UNKNOWN, builder); 258 259 assertFalse(success); 260 } 261 } 262