1 /* 2 * Copyright (C) 2023 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 android.view; 18 19 import static android.Manifest.permission.READ_FRAME_BUFFER; 20 import static android.content.pm.PackageManager.PERMISSION_DENIED; 21 22 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 23 24 import static org.junit.Assert.assertEquals; 25 import static org.junit.Assert.assertNotEquals; 26 import static org.junit.Assert.assertFalse; 27 import static org.junit.Assert.assertTrue; 28 import static org.junit.Assert.fail; 29 import static org.mockito.ArgumentMatchers.eq; 30 import static org.mockito.Mockito.doReturn; 31 import static org.mockito.Mockito.mock; 32 33 import android.content.Context; 34 import android.platform.test.annotations.Presubmit; 35 36 import androidx.test.runner.AndroidJUnit4; 37 38 import org.junit.AfterClass; 39 import org.junit.BeforeClass; 40 import org.junit.Test; 41 import org.junit.runner.RunWith; 42 43 import java.io.PrintWriter; 44 import java.util.WeakHashMap; 45 46 /** 47 * Class for testing {@link SurfaceControlRegistry}. 48 * 49 * Build/Install/Run: 50 * atest FrameworksCoreTests:android.view.SurfaceControlRegistryTests 51 */ 52 @Presubmit 53 @RunWith(AndroidJUnit4.class) 54 public class SurfaceControlRegistryTests { 55 56 @BeforeClass setUpOnce()57 public static void setUpOnce() { 58 SurfaceControlRegistry.createProcessInstance(getInstrumentation().getTargetContext()); 59 } 60 61 @AfterClass tearDownOnce()62 public static void tearDownOnce() { 63 SurfaceControlRegistry.destroyProcessInstance(); 64 } 65 66 @Test testRequiresPermissionToCreateProcessInstance()67 public void testRequiresPermissionToCreateProcessInstance() { 68 try { 69 Context ctx = mock(Context.class); 70 doReturn(PERMISSION_DENIED).when(ctx).checkSelfPermission(eq(READ_FRAME_BUFFER)); 71 SurfaceControlRegistry.createProcessInstance(ctx); 72 fail("Expected SecurityException due to missing permission"); 73 } catch (SecurityException se) { 74 // Expected failure 75 } catch (Exception e) { 76 fail("Unexpected exception: " + e); 77 } 78 } 79 80 @Test testCreateReleaseSurfaceControl()81 public void testCreateReleaseSurfaceControl() { 82 int hash0 = SurfaceControlRegistry.getProcessInstance().hashCode(); 83 SurfaceControl sc = buildTestSurface(); 84 assertNotEquals(hash0, SurfaceControlRegistry.getProcessInstance().hashCode()); 85 sc.release(); 86 assertEquals(hash0, SurfaceControlRegistry.getProcessInstance().hashCode()); 87 } 88 89 @Test testCreateReleaseMultipleSurfaceControls()90 public void testCreateReleaseMultipleSurfaceControls() { 91 int hash0 = SurfaceControlRegistry.getProcessInstance().hashCode(); 92 SurfaceControl sc1 = buildTestSurface(); 93 int hash1 = SurfaceControlRegistry.getProcessInstance().hashCode(); 94 assertNotEquals(hash0, hash1); 95 SurfaceControl sc2 = buildTestSurface(); 96 int hash1_2 = SurfaceControlRegistry.getProcessInstance().hashCode(); 97 assertNotEquals(hash0, hash1_2); 98 assertNotEquals(hash1, hash1_2); 99 // Release in inverse order to verify hashes still differ 100 sc1.release(); 101 int hash2 = SurfaceControlRegistry.getProcessInstance().hashCode(); 102 assertNotEquals(hash0, hash2); 103 sc2.release(); 104 assertEquals(hash0, SurfaceControlRegistry.getProcessInstance().hashCode()); 105 } 106 107 @Test testInvalidSurfaceControlNotAddedToRegistry()108 public void testInvalidSurfaceControlNotAddedToRegistry() { 109 int hash0 = SurfaceControlRegistry.getProcessInstance().hashCode(); 110 // Verify no changes to the registry when dealing with invalid surface controls 111 SurfaceControl sc0 = new SurfaceControl(); 112 SurfaceControl sc1 = new SurfaceControl(sc0, "test"); 113 assertEquals(hash0, SurfaceControlRegistry.getProcessInstance().hashCode()); 114 sc0.release(); 115 sc1.release(); 116 assertEquals(hash0, SurfaceControlRegistry.getProcessInstance().hashCode()); 117 } 118 119 @Test testThresholds()120 public void testThresholds() { 121 SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance(); 122 TestReporter reporter = new TestReporter(); 123 registry.setReportingThresholds(4 /* max */, 2 /* reset */, reporter); 124 125 // Exceed the threshold ensure the callback is made 126 SurfaceControl sc1 = buildTestSurface(); 127 SurfaceControl sc2 = buildTestSurface(); 128 SurfaceControl sc3 = buildTestSurface(); 129 SurfaceControl sc4 = buildTestSurface(); 130 reporter.assertMaxThresholdExceededCallCount(1); 131 reporter.assertLastReportedSetEquals(sc1, sc2, sc3, sc4); 132 133 // Create a few more, ensure we don't report again for the time being 134 SurfaceControl sc5 = buildTestSurface(); 135 SurfaceControl sc6 = buildTestSurface(); 136 reporter.assertMaxThresholdExceededCallCount(1); 137 reporter.assertLastReportedSetEquals(sc1, sc2, sc3, sc4); 138 139 // Release down to the reset threshold 140 sc1.release(); 141 sc2.release(); 142 sc3.release(); 143 sc4.release(); 144 145 // Create a few more to hit the max threshold again 146 SurfaceControl sc7 = buildTestSurface(); 147 SurfaceControl sc8 = buildTestSurface(); 148 reporter.assertMaxThresholdExceededCallCount(2); 149 reporter.assertLastReportedSetEquals(sc5, sc6, sc7, sc8); 150 } 151 152 @Test testCallStackDebugging_matchesFilters()153 public void testCallStackDebugging_matchesFilters() { 154 SurfaceControlRegistry registry = SurfaceControlRegistry.getProcessInstance(); 155 156 // Specific name, any call 157 registry.setCallStackDebuggingParams("com.android.app1", ""); 158 assertFalse(registry.matchesForCallStackDebugging("com.android.noMatchApp", "setAlpha")); 159 assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha")); 160 161 // Any name, specific call 162 registry.setCallStackDebuggingParams("", "setAlpha"); 163 assertFalse(registry.matchesForCallStackDebugging("com.android.app1", "setLayer")); 164 assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha")); 165 assertTrue(registry.matchesForCallStackDebugging("com.android.app2", "setAlpha")); 166 167 // Specific name, specific call 168 registry.setCallStackDebuggingParams("com.android.app1", "setAlpha"); 169 assertFalse(registry.matchesForCallStackDebugging("com.android.app1", "setLayer")); 170 assertFalse(registry.matchesForCallStackDebugging("com.android.app2", "setAlpha")); 171 assertTrue(registry.matchesForCallStackDebugging("com.android.app1", "setAlpha")); 172 } 173 buildTestSurface()174 private SurfaceControl buildTestSurface() { 175 return new SurfaceControl.Builder() 176 .setContainerLayer() 177 .setName("SurfaceControlRegistryTests") 178 .setCallsite("SurfaceControlRegistryTests") 179 .build(); 180 } 181 182 private static class TestReporter implements SurfaceControlRegistry.Reporter { 183 WeakHashMap<SurfaceControl, Long> lastSurfaceControls = new WeakHashMap<>(); 184 int callCount = 0; 185 186 @Override onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls, int limit, PrintWriter pw)187 public void onMaxLayersExceeded(WeakHashMap<SurfaceControl, Long> surfaceControls, 188 int limit, PrintWriter pw) { 189 lastSurfaceControls.clear(); 190 lastSurfaceControls.putAll(surfaceControls); 191 callCount++; 192 } 193 assertMaxThresholdExceededCallCount(int count)194 public void assertMaxThresholdExceededCallCount(int count) { 195 assertTrue("Expected " + count + " got " + callCount, count == callCount); 196 } 197 assertLastReportedSetEquals(SurfaceControl... surfaces)198 public void assertLastReportedSetEquals(SurfaceControl... surfaces) { 199 WeakHashMap<SurfaceControl, Long> last = new WeakHashMap<>(lastSurfaceControls); 200 for (int i = 0; i < surfaces.length; i++) { 201 last.remove(surfaces[i]); 202 } 203 if (!last.isEmpty()) { 204 fail("Sets differ"); 205 } 206 } 207 } 208 } 209