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