1 /* 2 * Copyright (C) 2017 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.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 21 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS; 22 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS; 23 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertFalse; 28 import static org.junit.Assert.assertNotNull; 29 import static org.junit.Assert.assertNull; 30 import static org.junit.Assert.assertTrue; 31 import static org.mockito.Mockito.mock; 32 import static org.mockito.Mockito.when; 33 34 import android.app.ActivityManager; 35 import android.content.res.Configuration; 36 import android.graphics.Rect; 37 import android.os.SystemClock; 38 import android.platform.test.annotations.Presubmit; 39 import android.util.ArraySet; 40 import android.view.Surface; 41 import android.window.TaskSnapshot; 42 43 import androidx.test.filters.MediumTest; 44 45 import com.android.server.wm.AppSnapshotLoader.PreRLegacySnapshotConfig; 46 import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider; 47 import com.android.server.wm.TaskSnapshotPersister.RemoveObsoleteFilesQueueItem; 48 49 import org.junit.Test; 50 import org.junit.runner.RunWith; 51 import org.mockito.MockitoSession; 52 53 import java.io.File; 54 55 /** 56 * Test class for {@link TaskSnapshotPersister} and {@link AppSnapshotLoader} 57 * 58 * Build/Install/Run: 59 * atest TaskSnapshotPersisterLoaderTest 60 */ 61 @MediumTest 62 @Presubmit 63 @RunWith(WindowTestRunner.class) 64 public class TaskSnapshotPersisterLoaderTest extends TaskSnapshotPersisterTestBase { 65 66 private static final float DELTA = 0.00001f; 67 68 private static final Rect TEST_INSETS = new Rect(10, 20, 30, 40); 69 TaskSnapshotPersisterLoaderTest()70 public TaskSnapshotPersisterLoaderTest() { 71 super(0.8f, 0.5f); 72 } 73 74 @Test testPersistAndLoadSnapshot()75 public void testPersistAndLoadSnapshot() { 76 mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); 77 mSnapshotPersistQueue.waitForQueueEmpty(); 78 final File[] files = new File[]{new File(FILES_DIR.getPath() + "/snapshots/1.proto"), 79 new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), 80 new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; 81 assertTrueForFiles(files, File::exists, " must exist"); 82 final TaskSnapshot snapshot = mLoader.loadTask(1, mTestUserId, false /* isLowResolution */); 83 assertNotNull(snapshot); 84 assertEquals(MOCK_SNAPSHOT_ID, snapshot.getId()); 85 assertEquals(TEST_INSETS, snapshot.getContentInsets()); 86 assertNotNull(snapshot.getSnapshot()); 87 assertEquals(Configuration.ORIENTATION_PORTRAIT, snapshot.getOrientation()); 88 89 snapshot.getHardwareBuffer().close(); 90 mPersister.persistSnapshot(1, mTestUserId, snapshot); 91 mSnapshotPersistQueue.waitForQueueEmpty(); 92 assertTrueForFiles(files, file -> !file.exists(), 93 " snapshot files must be removed by invalid buffer"); 94 } 95 96 @Test testTaskRemovedFromRecents()97 public void testTaskRemovedFromRecents() { 98 mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); 99 mPersister.onTaskRemovedFromRecents(1, mTestUserId); 100 mSnapshotPersistQueue.waitForQueueEmpty(); 101 assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.proto").exists()); 102 assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1.jpg").exists()); 103 assertFalse(new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg").exists()); 104 } 105 106 /** 107 * Tests that persisting a couple of snapshots is being throttled. 108 */ 109 @Test testThrottling()110 public void testThrottling() { 111 long ms = SystemClock.elapsedRealtime(); 112 mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); 113 mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); 114 mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId}); 115 mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId}); 116 mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId}); 117 mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId}); 118 mSnapshotPersistQueue.waitForQueueEmpty(); 119 assertTrue(SystemClock.elapsedRealtime() - ms > 500); 120 } 121 122 /** 123 * Tests that too many store write queue items are being purged. 124 */ 125 @Test testPurging()126 public void testPurging() { 127 mPersister.persistSnapshot(100, mTestUserId, createSnapshot()); 128 mSnapshotPersistQueue.waitForQueueEmpty(); 129 mSnapshotPersistQueue.setPaused(true); 130 mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); 131 mPersister.removeObsoleteFiles(new ArraySet<>(), new int[]{mTestUserId}); 132 mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); 133 mPersister.persistSnapshot(3, mTestUserId, createSnapshot()); 134 mPersister.persistSnapshot(4, mTestUserId, createSnapshot()); 135 mSnapshotPersistQueue.setPaused(false); 136 mSnapshotPersistQueue.waitForQueueEmpty(); 137 138 // Make sure 1,2 were purged but removeObsoleteFiles wasn't. 139 final File[] existsFiles = new File[]{ 140 new File(FILES_DIR.getPath() + "/snapshots/3.proto"), 141 new File(FILES_DIR.getPath() + "/snapshots/4.proto")}; 142 final File[] nonExistsFiles = new File[]{ 143 new File(FILES_DIR.getPath() + "/snapshots/100.proto"), 144 new File(FILES_DIR.getPath() + "/snapshots/1.proto"), 145 new File(FILES_DIR.getPath() + "/snapshots/1.proto")}; 146 assertTrueForFiles(existsFiles, File::exists, " must exist"); 147 assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); 148 } 149 150 @Test testGetTaskId()151 public void testGetTaskId() { 152 PersistInfoProvider persistInfoProvider = mock(PersistInfoProvider.class); 153 RemoveObsoleteFilesQueueItem removeObsoleteFilesQueueItem = 154 mPersister.new RemoveObsoleteFilesQueueItem( 155 new ArraySet<>(), new int[]{}, persistInfoProvider); 156 assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("blablablulp")); 157 assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("nothing.err")); 158 assertEquals(-1, removeObsoleteFilesQueueItem.getTaskId("/invalid/")); 159 assertEquals(12, removeObsoleteFilesQueueItem.getTaskId("12.jpg")); 160 assertEquals(12, removeObsoleteFilesQueueItem.getTaskId("12.proto")); 161 assertEquals(1, removeObsoleteFilesQueueItem.getTaskId("1.jpg")); 162 assertEquals(1, removeObsoleteFilesQueueItem.getTaskId("1_reduced.jpg")); 163 } 164 165 @Test testLegacyPLowRamConfig()166 public void testLegacyPLowRamConfig() throws Exception { 167 MockitoSession mockSession = mockitoSession() 168 .initMocks(this) 169 .mockStatic(ActivityManager.class) 170 .startMocking(); 171 172 when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true); 173 174 // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, 175 // for any P low_ram device 176 final int taskWidth = 0; 177 final float legacyScale = 0f; 178 final boolean hasHighResFile = false; 179 180 PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( 181 taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); 182 assertNotNull(highResConf); 183 assertEquals(highResConf.mScale, 0.6f, DELTA); 184 assertTrue(highResConf.mForceLoadReducedJpeg); 185 186 PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( 187 taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); 188 assertNotNull(lowResConf); 189 assertEquals(lowResConf.mScale, 0.6f, DELTA); 190 assertTrue(lowResConf.mForceLoadReducedJpeg); 191 192 mockSession.finishMocking(); 193 } 194 195 @Test testLegacyPNonLowRamConfig()196 public void testLegacyPNonLowRamConfig() throws Exception { 197 MockitoSession mockSession = mockitoSession() 198 .initMocks(this) 199 .mockStatic(ActivityManager.class) 200 .startMocking(); 201 202 when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false); 203 204 // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, 205 // for any O device, or a P non-low_ram device 206 final int taskWidth = 0; 207 final float legacyScale = 0f; 208 final boolean hasHighResFile = true; 209 210 PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( 211 taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); 212 assertNotNull(highResConf); 213 assertEquals(highResConf.mScale, 1.0f, DELTA); 214 assertFalse(highResConf.mForceLoadReducedJpeg); 215 216 PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( 217 taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); 218 assertNotNull(lowResConf); 219 assertEquals(lowResConf.mScale, 0.5f, DELTA); 220 assertFalse(lowResConf.mForceLoadReducedJpeg); 221 222 mockSession.finishMocking(); 223 } 224 225 @Test testLegacyQLowRamConfig()226 public void testLegacyQLowRamConfig() throws Exception { 227 MockitoSession mockSession = mockitoSession() 228 .initMocks(this) 229 .mockStatic(ActivityManager.class) 230 .startMocking(); 231 232 when(ActivityManager.isLowRamDeviceStatic()).thenReturn(true); 233 234 // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, 235 // for any Q low_ram device 236 final int taskWidth = 0; 237 final float legacyScale = 0.6f; 238 final boolean hasHighResFile = false; 239 240 PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( 241 taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); 242 assertNotNull(highResConf); 243 assertEquals(highResConf.mScale, legacyScale, DELTA); 244 assertEquals(highResConf.mScale, 0.6f, DELTA); 245 assertTrue(highResConf.mForceLoadReducedJpeg); 246 247 PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( 248 taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); 249 assertNotNull(lowResConf); 250 assertEquals(lowResConf.mScale, legacyScale, DELTA); 251 assertEquals(lowResConf.mScale, 0.6f, DELTA); 252 assertTrue(lowResConf.mForceLoadReducedJpeg); 253 254 mockSession.finishMocking(); 255 } 256 257 @Test testLegacyQNonLowRamConfig()258 public void testLegacyQNonLowRamConfig() throws Exception { 259 MockitoSession mockSession = mockitoSession() 260 .initMocks(this) 261 .mockStatic(ActivityManager.class) 262 .startMocking(); 263 264 when(ActivityManager.isLowRamDeviceStatic()).thenReturn(false); 265 266 // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, 267 // for any Q non-low_ram device 268 final int taskWidth = 0; 269 final float legacyScale = 0.8f; 270 final boolean hasHighResFile = true; 271 272 PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( 273 taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); 274 assertNotNull(highResConf); 275 assertEquals(highResConf.mScale, legacyScale, DELTA); 276 assertEquals(highResConf.mScale, 0.8f, DELTA); 277 assertFalse(highResConf.mForceLoadReducedJpeg); 278 279 PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( 280 taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); 281 assertNotNull(lowResConf); 282 assertEquals(lowResConf.mScale, 0.5f * legacyScale, DELTA); 283 assertEquals(lowResConf.mScale, 0.5f * 0.8f, DELTA); 284 assertFalse(lowResConf.mForceLoadReducedJpeg); 285 286 mockSession.finishMocking(); 287 } 288 289 @Test testNonLegacyRConfig()290 public void testNonLegacyRConfig() throws Exception { 291 // taskWidth and legacyScale as would be defined in the proto, and presence of a *.jpg file, 292 // for any R device 293 final int taskWidth = 1440; 294 final float legacyScale = 0f; 295 final boolean hasHighResFile = true; 296 297 PreRLegacySnapshotConfig highResConf = mLoader.getLegacySnapshotConfig( 298 taskWidth, legacyScale, hasHighResFile, false /* loadLowResolutionBitmap */); 299 assertNull(highResConf); 300 301 PreRLegacySnapshotConfig lowResConf = mLoader.getLegacySnapshotConfig( 302 taskWidth, legacyScale, hasHighResFile, true /* loadLowResolutionBitmap */); 303 assertNull(lowResConf); 304 } 305 306 @Test testIsRealSnapshotPersistAndLoadSnapshot()307 public void testIsRealSnapshotPersistAndLoadSnapshot() { 308 TaskSnapshot a = new TaskSnapshotBuilder() 309 .setIsRealSnapshot(true) 310 .build(); 311 TaskSnapshot b = new TaskSnapshotBuilder() 312 .setIsRealSnapshot(false) 313 .build(); 314 assertTrue(a.isRealSnapshot()); 315 assertFalse(b.isRealSnapshot()); 316 mPersister.persistSnapshot(1, mTestUserId, a); 317 mPersister.persistSnapshot(2, mTestUserId, b); 318 mSnapshotPersistQueue.waitForQueueEmpty(); 319 final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, 320 false /* isLowResolution */); 321 final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, 322 false /* isLowResolution */); 323 assertNotNull(snapshotA); 324 assertNotNull(snapshotB); 325 assertTrue(snapshotA.isRealSnapshot()); 326 assertFalse(snapshotB.isRealSnapshot()); 327 } 328 329 @Test testWindowingModePersistAndLoadSnapshot()330 public void testWindowingModePersistAndLoadSnapshot() { 331 TaskSnapshot a = new TaskSnapshotBuilder() 332 .setWindowingMode(WINDOWING_MODE_FULLSCREEN) 333 .build(); 334 TaskSnapshot b = new TaskSnapshotBuilder() 335 .setWindowingMode(WINDOWING_MODE_PINNED) 336 .build(); 337 assertEquals(WINDOWING_MODE_FULLSCREEN, a.getWindowingMode()); 338 assertEquals(WINDOWING_MODE_PINNED, b.getWindowingMode()); 339 mPersister.persistSnapshot(1, mTestUserId, a); 340 mPersister.persistSnapshot(2, mTestUserId, b); 341 mSnapshotPersistQueue.waitForQueueEmpty(); 342 final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, 343 false /* isLowResolution */); 344 final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, 345 false /* isLowResolution */); 346 assertNotNull(snapshotA); 347 assertNotNull(snapshotB); 348 assertEquals(WINDOWING_MODE_FULLSCREEN, snapshotA.getWindowingMode()); 349 assertEquals(WINDOWING_MODE_PINNED, snapshotB.getWindowingMode()); 350 } 351 352 @Test testIsTranslucentPersistAndLoadSnapshot()353 public void testIsTranslucentPersistAndLoadSnapshot() { 354 TaskSnapshot a = new TaskSnapshotBuilder() 355 .setIsTranslucent(true) 356 .build(); 357 TaskSnapshot b = new TaskSnapshotBuilder() 358 .setIsTranslucent(false) 359 .build(); 360 assertTrue(a.isTranslucent()); 361 assertFalse(b.isTranslucent()); 362 mPersister.persistSnapshot(1, mTestUserId, a); 363 mPersister.persistSnapshot(2, mTestUserId, b); 364 mSnapshotPersistQueue.waitForQueueEmpty(); 365 final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, 366 false /* isLowResolution */); 367 final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, 368 false /* isLowResolution */); 369 assertNotNull(snapshotA); 370 assertNotNull(snapshotB); 371 assertTrue(snapshotA.isTranslucent()); 372 assertFalse(snapshotB.isTranslucent()); 373 } 374 375 @Test testAppearancePersistAndLoadSnapshot()376 public void testAppearancePersistAndLoadSnapshot() { 377 final int lightBarFlags = APPEARANCE_LIGHT_STATUS_BARS | APPEARANCE_LIGHT_NAVIGATION_BARS; 378 TaskSnapshot a = new TaskSnapshotBuilder() 379 .setSystemUiVisibility(0) 380 .build(); 381 TaskSnapshot b = new TaskSnapshotBuilder() 382 .setSystemUiVisibility(lightBarFlags) 383 .build(); 384 assertEquals(0, a.getAppearance()); 385 assertEquals(lightBarFlags, b.getAppearance()); 386 mPersister.persistSnapshot(1, mTestUserId, a); 387 mPersister.persistSnapshot(2, mTestUserId, b); 388 mSnapshotPersistQueue.waitForQueueEmpty(); 389 final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, 390 false /* isLowResolution */); 391 final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, 392 false /* isLowResolution */); 393 assertNotNull(snapshotA); 394 assertNotNull(snapshotB); 395 assertEquals(0, snapshotA.getAppearance()); 396 assertEquals(lightBarFlags, snapshotB.getAppearance()); 397 } 398 399 @Test testScalePersistAndLoadSnapshot()400 public void testScalePersistAndLoadSnapshot() { 401 TaskSnapshot a = new TaskSnapshotBuilder() 402 .setScaleFraction(0.25f) 403 .build(); 404 TaskSnapshot b = new TaskSnapshotBuilder() 405 .setScaleFraction(0.75f) 406 .build(); 407 mPersister.persistSnapshot(1, mTestUserId, a); 408 mPersister.persistSnapshot(2, mTestUserId, b); 409 mSnapshotPersistQueue.waitForQueueEmpty(); 410 final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, 411 false /* isLowResolution */); 412 final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, 413 false /* isLowResolution */); 414 assertNotNull(snapshotA); 415 assertNotNull(snapshotB); 416 } 417 418 @Test testRemoveObsoleteFiles()419 public void testRemoveObsoleteFiles() { 420 mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); 421 mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); 422 final ArraySet<Integer> taskIds = new ArraySet<>(); 423 taskIds.add(1); 424 mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); 425 mSnapshotPersistQueue.waitForQueueEmpty(); 426 final File[] existsFiles = new File[]{ 427 new File(FILES_DIR.getPath() + "/snapshots/1.proto"), 428 new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), 429 new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg")}; 430 final File[] nonExistsFiles = new File[]{ 431 new File(FILES_DIR.getPath() + "/snapshots/2.proto"), 432 new File(FILES_DIR.getPath() + "/snapshots/2.jpg"), 433 new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; 434 assertTrueForFiles(existsFiles, File::exists, " must exist"); 435 assertTrueForFiles(nonExistsFiles, file -> !file.exists(), " must not exist"); 436 } 437 438 @Test testRemoveObsoleteFiles_addedOneInTheMeantime()439 public void testRemoveObsoleteFiles_addedOneInTheMeantime() { 440 mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); 441 final ArraySet<Integer> taskIds = new ArraySet<>(); 442 taskIds.add(1); 443 mPersister.removeObsoleteFiles(taskIds, new int[]{mTestUserId}); 444 mPersister.persistSnapshot(2, mTestUserId, createSnapshot()); 445 mSnapshotPersistQueue.waitForQueueEmpty(); 446 final File[] existsFiles = new File[]{ 447 new File(FILES_DIR.getPath() + "/snapshots/1.proto"), 448 new File(FILES_DIR.getPath() + "/snapshots/1.jpg"), 449 new File(FILES_DIR.getPath() + "/snapshots/1_reduced.jpg"), 450 new File(FILES_DIR.getPath() + "/snapshots/2.proto"), 451 new File(FILES_DIR.getPath() + "/snapshots/2.jpg"), 452 new File(FILES_DIR.getPath() + "/snapshots/2_reduced.jpg")}; 453 assertTrueForFiles(existsFiles, File::exists, " must exist"); 454 } 455 456 @Test testRotationPersistAndLoadSnapshot()457 public void testRotationPersistAndLoadSnapshot() { 458 TaskSnapshot a = new TaskSnapshotBuilder() 459 .setRotation(Surface.ROTATION_270) 460 .build(); 461 mPersister.persistSnapshot(1, mTestUserId, createSnapshot()); 462 mPersister.persistSnapshot(2, mTestUserId, a); 463 mSnapshotPersistQueue.waitForQueueEmpty(); 464 final TaskSnapshot snapshotA = mLoader.loadTask(1, mTestUserId, 465 false /* isLowResolution */); 466 final TaskSnapshot snapshotB = mLoader.loadTask(2, mTestUserId, 467 false /* isLowResolution */); 468 assertEquals(Surface.ROTATION_0, snapshotA.getRotation()); 469 assertEquals(Surface.ROTATION_270, snapshotB.getRotation()); 470 } 471 } 472