1 /* 2 * Copyright (C) 2020 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.server.wm; 18 19 import static android.server.wm.WindowManagerTestBase.startActivity; 20 import static android.view.Display.DEFAULT_DISPLAY; 21 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 22 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 23 24 import static com.google.common.truth.Truth.assertThat; 25 26 import static org.junit.Assert.assertEquals; 27 import static org.junit.Assert.assertTrue; 28 29 import android.content.ComponentCallbacks; 30 import android.content.ComponentName; 31 import android.content.Context; 32 import android.content.Intent; 33 import android.content.ServiceConnection; 34 import android.content.res.Configuration; 35 import android.graphics.Rect; 36 import android.os.Binder; 37 import android.os.IBinder; 38 import android.platform.test.annotations.AppModeFull; 39 import android.platform.test.annotations.Presubmit; 40 import android.server.wm.WindowContextTests.TestWindowService.TestToken; 41 import android.view.View; 42 import android.view.WindowManager; 43 import android.window.WindowProviderService; 44 45 import androidx.annotation.NonNull; 46 import androidx.annotation.Nullable; 47 import androidx.test.core.app.ApplicationProvider; 48 import androidx.test.rule.ServiceTestRule; 49 50 import org.junit.Test; 51 52 import java.util.concurrent.CountDownLatch; 53 import java.util.concurrent.TimeUnit; 54 55 /** 56 * Tests that verify the behavior of window context 57 * 58 * Build/Install/Run: 59 * atest CtsWindowManagerDeviceTestCases:WindowContextTests 60 */ 61 @Presubmit 62 public class WindowContextTests extends WindowContextTestBase { 63 @Test 64 @AppModeFull testWindowContextConfigChanges()65 public void testWindowContextConfigChanges() { 66 createAllowSystemAlertWindowAppOpSession(); 67 final WindowManagerState.DisplayContent display = createManagedVirtualDisplaySession() 68 .setSimulateDisplay(true).createDisplay(); 69 final Context windowContext = createWindowContext(display.mId); 70 mInstrumentation.runOnMainSync(() -> { 71 final View view = new View(windowContext); 72 WindowManager wm = windowContext.getSystemService(WindowManager.class); 73 wm.addView(view, new WindowManager.LayoutParams(TYPE_APPLICATION_OVERLAY)); 74 }); 75 final DisplayMetricsSession displayMetricsSession = 76 createManagedDisplayMetricsSession(display.mId); 77 78 mWmState.computeState(); 79 80 Rect bounds = windowContext.getSystemService(WindowManager.class).getCurrentWindowMetrics() 81 .getBounds(); 82 assertBoundsEquals(displayMetricsSession.getDisplayMetrics(), bounds); 83 84 displayMetricsSession.changeDisplayMetrics(1.2 /* sizeRatio */, 1.1 /* densityRatio */); 85 86 mWmState.computeState(); 87 88 bounds = windowContext.getSystemService(WindowManager.class).getCurrentWindowMetrics() 89 .getBounds(); 90 assertBoundsEquals(displayMetricsSession.getDisplayMetrics(), bounds); 91 } 92 assertBoundsEquals(ReportedDisplayMetrics expectedMetrics, Rect bounds)93 private void assertBoundsEquals(ReportedDisplayMetrics expectedMetrics, 94 Rect bounds) { 95 assertEquals(expectedMetrics.getSize().getWidth(), bounds.width()); 96 assertEquals(expectedMetrics.getSize().getHeight(), bounds.height()); 97 } 98 99 @Test 100 @AppModeFull testWindowContextBindService()101 public void testWindowContextBindService() { 102 createAllowSystemAlertWindowAppOpSession(); 103 final Context windowContext = createWindowContext(DEFAULT_DISPLAY); 104 final ServiceConnection serviceConnection = new ServiceConnection() { 105 @Override 106 public void onServiceConnected(ComponentName name, IBinder service) {} 107 108 @Override 109 public void onServiceDisconnected(ComponentName name) {} 110 }; 111 try { 112 assertTrue("WindowContext must bind service successfully.", 113 windowContext.bindService(new Intent(windowContext, TestLogService.class), 114 serviceConnection, Context.BIND_AUTO_CREATE)); 115 } finally { 116 windowContext.unbindService(serviceConnection); 117 } 118 } 119 120 /** 121 * Verify if the {@link ComponentCallbacks#onConfigurationChanged(Configuration)} callback 122 * is received when the window context configuration changes. 123 */ 124 @Test testWindowContextRegisterComponentCallbacks()125 public void testWindowContextRegisterComponentCallbacks() throws Exception { 126 final TestComponentCallbacks callbacks = new TestComponentCallbacks(); 127 final WindowManagerState.DisplayContent display = createManagedVirtualDisplaySession() 128 .setSimulateDisplay(true).createDisplay(); 129 final Context windowContext = createWindowContext(display.mId); 130 final DisplayMetricsSession displayMetricsSession = 131 createManagedDisplayMetricsSession(display.mId); 132 133 windowContext.registerComponentCallbacks(callbacks); 134 135 callbacks.mLatch = new CountDownLatch(1); 136 137 displayMetricsSession.changeDisplayMetrics(1.2 /* sizeRatio */, 1.1 /* densityRatio */); 138 139 // verify if there is a callback from the window context configuration change. 140 assertTrue(callbacks.mLatch.await(4, TimeUnit.SECONDS)); 141 Rect bounds = callbacks.mConfiguration.windowConfiguration.getBounds(); 142 assertBoundsEquals(displayMetricsSession.getDisplayMetrics(), bounds); 143 144 windowContext.unregisterComponentCallbacks(callbacks); 145 } 146 147 /** 148 * Verifies if window context on the secondary display receives global configuration changes. 149 */ 150 @Test testWindowContextGlobalConfigChanges()151 public void testWindowContextGlobalConfigChanges() { 152 final TestComponentCallbacks callbacks = new TestComponentCallbacks(); 153 final WindowManagerState.DisplayContent display = createManagedVirtualDisplaySession() 154 .setPublicDisplay(true).createDisplay(); 155 final FontScaleSession fontScaleSession = createManagedFontScaleSession(); 156 final Context windowContext = createWindowContext(display.mId); 157 windowContext.registerComponentCallbacks(callbacks); 158 final float expectedFontScale = fontScaleSession.get() + 0.3f; 159 fontScaleSession.set(expectedFontScale); 160 161 // Wait for TestComponentCallbacks#mConfiguration to be assigned. 162 callbacks.waitForConfigChanged(); 163 164 // We don't rely on latch to verify the result because we may receive two configuration 165 // changes. One may from that WindowContext attaches to a DisplayArea although it is before 166 // ComponentCallback registration), the other is from font the scale change, which is what 167 // we want to verify. 168 waitForOrFail("font scale to match " + expectedFontScale, () -> 169 expectedFontScale == callbacks.mConfiguration.fontScale); 170 171 windowContext.unregisterComponentCallbacks(callbacks); 172 } 173 174 /** 175 * Verify the {@link WindowProviderService} lifecycle: 176 * <ul> 177 * <li>In {@link WindowProviderService#onCreate()}, register to the DisplayArea with 178 * given value from {@link WindowProviderService#getWindowType()} and 179 * {@link WindowProviderService#getWindowContextOptions()}} and receive a 180 * {@link Configuration} update which matches DisplayArea's metrics.</li> 181 * <li>After {@link WindowProviderService#attachToWindowToken(IBinder)}, the 182 * {@link WindowProviderService} must be switched to register to the Window Token and 183 * receive a configuration update which matches Window Token's metrics.</li> 184 * </ul> 185 */ 186 @Test testWindowProviderServiceLifecycle()187 public void testWindowProviderServiceLifecycle() throws Exception { 188 // Start an activity for WindowProviderService to attach 189 TestActivity activity = startActivity(TestActivity.class); 190 final ComponentName activityName = activity.getComponentName(); 191 192 // If the device supports multi-window, make this Activity to multi-window mode. 193 // In this way, we can verify if the WindowProviderService's metrics matches 194 // the split-screen Activity's metrics, which is different from TaskDisplayArea's metrics. 195 if (supportsSplitScreenMultiWindow()) { 196 mWmState.computeState(activityName); 197 198 putActivityInPrimarySplit(activityName); 199 200 activity.waitAndAssertConfigurationChanged(); 201 } 202 203 // Obtain the TestWindowService instance. 204 final Context context = ApplicationProvider.getApplicationContext(); 205 final Intent intent = new Intent(context, TestWindowService.class); 206 final ServiceTestRule serviceRule = new ServiceTestRule(); 207 try { 208 TestToken token = (TestToken) serviceRule.bindService(intent); 209 final TestWindowService service = token.getService(); 210 211 mWmState.computeState(activityName); 212 final WindowManagerState.DisplayArea da = mWmState.getTaskDisplayArea(activityName); 213 final Rect daBounds = da.mFullConfiguration.windowConfiguration.getBounds(); 214 final Rect maxDaBounds = da.mFullConfiguration.windowConfiguration.getMaxBounds(); 215 216 waitAndAssertWindowMetricsBoundsMatches(service, daBounds, maxDaBounds, 217 "WindowProviderService bounds must match DisplayArea bounds."); 218 219 // Obtain the Activity's token and attach it to TestWindowService. 220 final IBinder windowToken = activity.getWindow().getAttributes().token; 221 service.attachToWindowToken(windowToken); 222 223 final WindowManager wm = activity.getWindowManager(); 224 final Rect currentBounds = wm.getCurrentWindowMetrics().getBounds(); 225 final Rect maxBounds = wm.getMaximumWindowMetrics().getBounds(); 226 227 // After TestWindowService attaches the Activity's token, which is also a WindowToken, 228 // it is expected to receive a config update which matches the WindowMetrics of 229 // the Activity. 230 waitAndAssertWindowMetricsBoundsMatches(service, currentBounds, maxBounds, 231 "WindowProviderService bounds must match WindowToken bounds."); 232 } finally { 233 serviceRule.unbindService(); 234 } 235 } 236 waitAndAssertWindowMetricsBoundsMatches(Context context, Rect currentBounds, Rect maxBounds, String message)237 private void waitAndAssertWindowMetricsBoundsMatches(Context context, Rect currentBounds, 238 Rect maxBounds, String message) { 239 final WindowManager wm = context.getSystemService(WindowManager.class); 240 waitForOrFail(message, () -> { 241 final Rect currentWindowBounds = wm.getCurrentWindowMetrics().getBounds(); 242 final Rect maxWindowBounds = wm.getMaximumWindowMetrics().getBounds(); 243 return currentBounds.equals(currentWindowBounds) && maxBounds.equals(maxWindowBounds); 244 }); 245 } 246 247 public static class TestActivity extends WindowManagerTestBase.FocusableActivity { 248 final CountDownLatch mLatch = new CountDownLatch(1); 249 250 @Override onConfigurationChanged(@onNull Configuration newConfig)251 public void onConfigurationChanged(@NonNull Configuration newConfig) { 252 super.onConfigurationChanged(newConfig); 253 mLatch.countDown(); 254 } 255 waitAndAssertConfigurationChanged()256 private void waitAndAssertConfigurationChanged() throws Exception { 257 assertThat(mLatch.await(4, TimeUnit.SECONDS)).isTrue(); 258 } 259 } 260 261 public static class TestWindowService extends WindowProviderService { 262 private final IBinder mToken = new TestToken(); 263 264 @Override getWindowType()265 public int getWindowType() { 266 return TYPE_APPLICATION; 267 } 268 269 @Nullable 270 @Override onBind(Intent intent)271 public IBinder onBind(Intent intent) { 272 return mToken; 273 } 274 275 public class TestToken extends Binder { getService()276 private TestWindowService getService() { 277 return TestWindowService.this; 278 } 279 } 280 } 281 282 private static class TestComponentCallbacks implements ComponentCallbacks { 283 private Configuration mConfiguration; 284 private CountDownLatch mLatch = new CountDownLatch(1); 285 waitForConfigChanged()286 private void waitForConfigChanged() { 287 try { 288 assertThat(mLatch.await(4, TimeUnit.SECONDS)).isTrue(); 289 } catch (InterruptedException e) { 290 throw new RuntimeException(e); 291 } 292 } 293 294 @Override onConfigurationChanged(@onNull Configuration newConfig)295 public void onConfigurationChanged(@NonNull Configuration newConfig) { 296 mConfiguration = newConfig; 297 mLatch.countDown(); 298 } 299 300 @Override onLowMemory()301 public void onLowMemory() {} 302 } 303 } 304