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.content.res.Configuration.ORIENTATION_PORTRAIT; 21 import static android.graphics.GraphicBuffer.USAGE_HW_TEXTURE; 22 import static android.graphics.GraphicBuffer.USAGE_SW_READ_RARELY; 23 24 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 25 26 import static org.junit.Assert.assertTrue; 27 import static org.mockito.Mockito.mock; 28 import static org.mockito.Mockito.spy; 29 import static org.mockito.Mockito.when; 30 31 import android.annotation.NonNull; 32 import android.content.ComponentName; 33 import android.content.ContextWrapper; 34 import android.content.res.Resources; 35 import android.graphics.Canvas; 36 import android.graphics.Color; 37 import android.graphics.ColorSpace; 38 import android.graphics.GraphicBuffer; 39 import android.graphics.PixelFormat; 40 import android.graphics.Point; 41 import android.graphics.Rect; 42 import android.hardware.HardwareBuffer; 43 import android.os.UserManager; 44 import android.view.Surface; 45 import android.window.TaskSnapshot; 46 47 import com.android.server.LocalServices; 48 import com.android.server.pm.UserManagerInternal; 49 import com.android.server.wm.BaseAppSnapshotPersister.PersistInfoProvider; 50 import com.android.window.flags.Flags; 51 52 import org.junit.After; 53 import org.junit.AfterClass; 54 import org.junit.Before; 55 import org.junit.BeforeClass; 56 57 import java.io.File; 58 import java.util.function.Predicate; 59 60 /** 61 * Base class for tests that use a {@link TaskSnapshotPersister}. 62 */ 63 class TaskSnapshotPersisterTestBase extends WindowTestsBase { 64 65 private static final Rect TEST_CONTENT_INSETS = new Rect(10, 20, 30, 40); 66 private static final Rect TEST_LETTERBOX_INSETS = new Rect(); 67 static final File FILES_DIR = getInstrumentation().getTargetContext().getFilesDir(); 68 static final long MOCK_SNAPSHOT_ID = 12345678; 69 70 private ContextWrapper mContextSpy; 71 private Resources mResourcesSpy; 72 SnapshotPersistQueue mSnapshotPersistQueue; 73 TaskSnapshotPersister mPersister; 74 AppSnapshotLoader mLoader; 75 int mTestUserId; 76 float mHighResScale; 77 float mLowResScale; 78 TaskSnapshotPersisterTestBase(float highResScale, float lowResScale)79 TaskSnapshotPersisterTestBase(float highResScale, float lowResScale) { 80 super(); 81 mHighResScale = highResScale; 82 mLowResScale = lowResScale; 83 } 84 85 @BeforeClass setUpOnce()86 public static void setUpOnce() { 87 final UserManagerInternal userManager = mock(UserManagerInternal.class); 88 LocalServices.addService(UserManagerInternal.class, userManager); 89 } 90 91 @AfterClass tearDownOnce()92 public static void tearDownOnce() { 93 LocalServices.removeServiceForTest(UserManagerInternal.class); 94 } 95 96 @Before setUp()97 public void setUp() { 98 final UserManager um = UserManager.get(getInstrumentation().getTargetContext()); 99 mTestUserId = um.getProcessUserId(); 100 101 final UserManagerInternal userManagerInternal = 102 LocalServices.getService(UserManagerInternal.class); 103 when(userManagerInternal.isUserUnlocked(mTestUserId)).thenReturn(true); 104 105 mContextSpy = spy(new ContextWrapper(mWm.mContext)); 106 mResourcesSpy = spy(mContextSpy.getResources()); 107 when(mContextSpy.getResources()).thenReturn(mResourcesSpy); 108 when(mResourcesSpy.getFloat( 109 com.android.internal.R.dimen.config_highResTaskSnapshotScale)) 110 .thenReturn(mHighResScale); 111 when(mResourcesSpy.getFloat( 112 com.android.internal.R.dimen.config_lowResTaskSnapshotScale)) 113 .thenReturn(mLowResScale); 114 115 mSnapshotPersistQueue = new SnapshotPersistQueue(); 116 PersistInfoProvider provider = 117 TaskSnapshotController.createPersistInfoProvider(mWm, userId -> FILES_DIR); 118 mPersister = new TaskSnapshotPersister(mSnapshotPersistQueue, provider, false); 119 mLoader = new AppSnapshotLoader(provider); 120 mSnapshotPersistQueue.start(); 121 } 122 123 @After tearDown()124 public void tearDown() { 125 cleanDirectory(); 126 } 127 cleanDirectory()128 private void cleanDirectory() { 129 final File[] files = new File(FILES_DIR, "snapshots").listFiles(); 130 if (files == null) { 131 return; 132 } 133 for (File file : files) { 134 if (file.isDirectory()) { 135 final File[] subFiles = file.listFiles(); 136 if (subFiles == null) { 137 continue; 138 } 139 for (File subFile : subFiles) { 140 subFile.delete(); 141 } 142 } 143 file.delete(); 144 } 145 } 146 convertFilePath(@onNull String... fileNames)147 File[] convertFilePath(@NonNull String... fileNames) { 148 final File[] files = new File[fileNames.length]; 149 final String path; 150 if (Flags.scrambleSnapshotFileName()) { 151 path = mPersister.mPersistInfoProvider.getDirectory(mTestUserId).getPath(); 152 } else { 153 path = FILES_DIR.getPath() + "/snapshots/"; 154 } 155 for (int i = 0; i < fileNames.length; i++) { 156 files[i] = new File(path, fileNames[i]); 157 } 158 return files; 159 } 160 createSnapshot()161 TaskSnapshot createSnapshot() { 162 return new TaskSnapshotBuilder().setTopActivityComponent(getUniqueComponentName()).build(); 163 } 164 assertTrueForFiles(File[] files, Predicate<File> predicate, String message)165 protected static void assertTrueForFiles(File[] files, Predicate<File> predicate, 166 String message) { 167 for (File file : files) { 168 assertTrue(file.getName() + message, predicate.test(file)); 169 } 170 } 171 172 /** 173 * Builds a TaskSnapshot. 174 */ 175 static class TaskSnapshotBuilder { 176 private static final int SNAPSHOT_WIDTH = 100; 177 private static final int SNAPSHOT_HEIGHT = 100; 178 179 private float mScaleFraction = 1f; 180 private boolean mIsRealSnapshot = true; 181 private boolean mIsTranslucent = false; 182 private int mWindowingMode = WINDOWING_MODE_FULLSCREEN; 183 private int mSystemUiVisibility = 0; 184 private int mRotation = Surface.ROTATION_0; 185 private int mWidth = SNAPSHOT_WIDTH; 186 private int mHeight = SNAPSHOT_HEIGHT; 187 private ComponentName mTopActivityComponent = new ComponentName("", ""); 188 TaskSnapshotBuilder()189 TaskSnapshotBuilder() { 190 } 191 setTopActivityComponent(ComponentName topActivityComponent)192 TaskSnapshotBuilder setTopActivityComponent(ComponentName topActivityComponent) { 193 mTopActivityComponent = topActivityComponent; 194 return this; 195 } 196 setScaleFraction(float scale)197 TaskSnapshotBuilder setScaleFraction(float scale) { 198 mScaleFraction = scale; 199 return this; 200 } 201 setIsRealSnapshot(boolean isRealSnapshot)202 TaskSnapshotBuilder setIsRealSnapshot(boolean isRealSnapshot) { 203 mIsRealSnapshot = isRealSnapshot; 204 return this; 205 } 206 setIsTranslucent(boolean isTranslucent)207 TaskSnapshotBuilder setIsTranslucent(boolean isTranslucent) { 208 mIsTranslucent = isTranslucent; 209 return this; 210 } 211 setWindowingMode(int windowingMode)212 TaskSnapshotBuilder setWindowingMode(int windowingMode) { 213 mWindowingMode = windowingMode; 214 return this; 215 } 216 setSystemUiVisibility(int systemUiVisibility)217 TaskSnapshotBuilder setSystemUiVisibility(int systemUiVisibility) { 218 mSystemUiVisibility = systemUiVisibility; 219 return this; 220 } 221 setRotation(int rotation)222 TaskSnapshotBuilder setRotation(int rotation) { 223 mRotation = rotation; 224 return this; 225 } 226 setTaskSize(int width, int height)227 TaskSnapshotBuilder setTaskSize(int width, int height) { 228 mWidth = width; 229 mHeight = height; 230 return this; 231 } 232 build()233 TaskSnapshot build() { 234 // To satisfy existing tests, ensure the graphics buffer is always 100x100, and 235 // compute the ize of the task according to mScaleFraction. 236 Point taskSize = new Point((int) (mWidth / mScaleFraction), 237 (int) (mHeight / mScaleFraction)); 238 final GraphicBuffer buffer = GraphicBuffer.create(mWidth, mHeight, 239 PixelFormat.RGBA_8888, 240 USAGE_HW_TEXTURE | USAGE_SW_READ_RARELY | USAGE_SW_READ_RARELY); 241 Canvas c = buffer.lockCanvas(); 242 c.drawColor(Color.RED); 243 buffer.unlockCanvasAndPost(c); 244 return new TaskSnapshot(MOCK_SNAPSHOT_ID, 0 /* captureTime */, mTopActivityComponent, 245 HardwareBuffer.createFromGraphicBuffer(buffer), 246 ColorSpace.get(ColorSpace.Named.SRGB), ORIENTATION_PORTRAIT, 247 mRotation, taskSize, TEST_CONTENT_INSETS, TEST_LETTERBOX_INSETS, 248 // When building a TaskSnapshot with the Builder class, isLowResolution 249 // is always false. Low-res snapshots are only created when loading from 250 // disk. 251 false /* isLowResolution */, 252 mIsRealSnapshot, mWindowingMode, mSystemUiVisibility, mIsTranslucent, 253 false /* hasImeSurface */, 0 /* uiMode */); 254 } 255 } 256 } 257