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.ACTIVITY_TYPE_HOME; 20 import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD; 21 import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED; 22 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 23 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 24 import static android.app.WindowConfiguration.WINDOWING_MODE_MULTI_WINDOW; 25 import static android.app.WindowConfiguration.WINDOWING_MODE_UNDEFINED; 26 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; 27 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; 28 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; 29 import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; 30 import static android.content.res.Configuration.EMPTY; 31 import static android.content.res.Configuration.SMALLEST_SCREEN_WIDTH_DP_UNDEFINED; 32 33 import static org.junit.Assert.assertEquals; 34 import static org.junit.Assert.assertTrue; 35 36 import android.content.res.Configuration; 37 import android.graphics.Rect; 38 import android.platform.test.annotations.Presubmit; 39 40 import androidx.test.filters.SmallTest; 41 42 import org.junit.Test; 43 44 import java.util.ArrayList; 45 import java.util.List; 46 47 /** 48 * Test class for {@link ConfigurationContainer}. 49 * 50 * Build/Install/Run: 51 * atest WmTests:ConfigurationContainerTests 52 */ 53 @SmallTest 54 @Presubmit 55 public class ConfigurationContainerTests { 56 57 @Test testConfigurationInit()58 public void testConfigurationInit() { 59 // Check root container initial config. 60 final TestConfigurationContainer root = new TestConfigurationContainer(); 61 assertEquals(EMPTY, root.getRequestedOverrideConfiguration()); 62 assertEquals(EMPTY, root.getMergedOverrideConfiguration()); 63 assertEquals(EMPTY, root.getConfiguration()); 64 65 // Check child initial config. 66 final TestConfigurationContainer child1 = root.addChild(); 67 assertEquals(EMPTY, child1.getRequestedOverrideConfiguration()); 68 assertEquals(EMPTY, child1.getMergedOverrideConfiguration()); 69 assertEquals(EMPTY, child1.getConfiguration()); 70 71 // Check child initial config if root has overrides. 72 final Configuration rootOverrideConfig = new Configuration(); 73 rootOverrideConfig.fontScale = 1.3f; 74 root.onRequestedOverrideConfigurationChanged(rootOverrideConfig); 75 final TestConfigurationContainer child2 = root.addChild(); 76 assertEquals(EMPTY, child2.getRequestedOverrideConfiguration()); 77 assertEquals(rootOverrideConfig, child2.getMergedOverrideConfiguration()); 78 assertEquals(rootOverrideConfig, child2.getConfiguration()); 79 80 // Check child initial config if root has parent config set. 81 final Configuration rootParentConfig = new Configuration(); 82 rootParentConfig.fontScale = 0.8f; 83 rootParentConfig.orientation = SCREEN_ORIENTATION_LANDSCAPE; 84 root.onConfigurationChanged(rootParentConfig); 85 final Configuration rootFullConfig = new Configuration(rootParentConfig); 86 rootFullConfig.updateFrom(rootOverrideConfig); 87 88 final TestConfigurationContainer child3 = root.addChild(); 89 assertEquals(EMPTY, child3.getRequestedOverrideConfiguration()); 90 assertEquals(rootOverrideConfig, child3.getMergedOverrideConfiguration()); 91 assertEquals(rootFullConfig, child3.getConfiguration()); 92 } 93 94 @Test testConfigurationChangeOnAddRemove()95 public void testConfigurationChangeOnAddRemove() { 96 // Init root's config. 97 final TestConfigurationContainer root = new TestConfigurationContainer(); 98 final Configuration rootOverrideConfig = new Configuration(); 99 rootOverrideConfig.fontScale = 1.3f; 100 root.onRequestedOverrideConfigurationChanged(rootOverrideConfig); 101 102 // Init child's config. 103 final TestConfigurationContainer child = root.addChild(); 104 final Configuration childOverrideConfig = new Configuration(); 105 childOverrideConfig.densityDpi = 320; 106 child.onRequestedOverrideConfigurationChanged(childOverrideConfig); 107 final Configuration mergedOverrideConfig = new Configuration(root.getConfiguration()); 108 mergedOverrideConfig.updateFrom(childOverrideConfig); 109 110 // Check configuration update when child is removed from parent. 111 root.removeChild(child); 112 assertEquals(childOverrideConfig, child.getRequestedOverrideConfiguration()); 113 assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration()); 114 assertEquals(mergedOverrideConfig, child.getConfiguration()); 115 116 // It may be paranoia... but let's check if parent's config didn't change after removal. 117 assertEquals(rootOverrideConfig, root.getRequestedOverrideConfiguration()); 118 assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration()); 119 assertEquals(rootOverrideConfig, root.getConfiguration()); 120 121 // Init different root 122 final TestConfigurationContainer root2 = new TestConfigurationContainer(); 123 final Configuration rootOverrideConfig2 = new Configuration(); 124 rootOverrideConfig2.fontScale = 1.1f; 125 root2.onRequestedOverrideConfigurationChanged(rootOverrideConfig2); 126 127 // Check configuration update when child is added to different parent. 128 mergedOverrideConfig.setTo(rootOverrideConfig2); 129 mergedOverrideConfig.updateFrom(childOverrideConfig); 130 root2.addChild(child); 131 assertEquals(childOverrideConfig, child.getRequestedOverrideConfiguration()); 132 assertEquals(mergedOverrideConfig, child.getMergedOverrideConfiguration()); 133 assertEquals(mergedOverrideConfig, child.getConfiguration()); 134 } 135 136 @Test testConfigurationChangePropagation()137 public void testConfigurationChangePropagation() { 138 // Builds 3-level vertical hierarchy with one configuration container on each level. 139 // In addition to different overrides on each level, everyone in hierarchy will have one 140 // common overridden value - orientation; 141 142 // Init root's config. 143 final TestConfigurationContainer root = new TestConfigurationContainer(); 144 final Configuration rootOverrideConfig = new Configuration(); 145 rootOverrideConfig.fontScale = 1.3f; 146 rootOverrideConfig.orientation = SCREEN_ORIENTATION_REVERSE_LANDSCAPE; 147 root.onRequestedOverrideConfigurationChanged(rootOverrideConfig); 148 149 // Init children. 150 final TestConfigurationContainer child1 = root.addChild(); 151 final Configuration childOverrideConfig1 = new Configuration(); 152 childOverrideConfig1.densityDpi = 320; 153 childOverrideConfig1.orientation = SCREEN_ORIENTATION_LANDSCAPE; 154 child1.onRequestedOverrideConfigurationChanged(childOverrideConfig1); 155 156 final TestConfigurationContainer child2 = child1.addChild(); 157 final Configuration childOverrideConfig2 = new Configuration(); 158 childOverrideConfig2.screenWidthDp = 150; 159 childOverrideConfig2.orientation = SCREEN_ORIENTATION_PORTRAIT; 160 child2.onRequestedOverrideConfigurationChanged(childOverrideConfig2); 161 162 // Check configuration on all levels when root override is updated. 163 rootOverrideConfig.smallestScreenWidthDp = 200; 164 root.onRequestedOverrideConfigurationChanged(rootOverrideConfig); 165 166 final Configuration mergedOverrideConfig1 = new Configuration(rootOverrideConfig); 167 mergedOverrideConfig1.updateFrom(childOverrideConfig1); 168 final Configuration mergedConfig1 = new Configuration(mergedOverrideConfig1); 169 170 final Configuration mergedOverrideConfig2 = new Configuration(mergedOverrideConfig1); 171 mergedOverrideConfig2.updateFrom(childOverrideConfig2); 172 final Configuration mergedConfig2 = new Configuration(mergedOverrideConfig2); 173 174 assertEquals(rootOverrideConfig, root.getRequestedOverrideConfiguration()); 175 assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration()); 176 assertEquals(rootOverrideConfig, root.getConfiguration()); 177 178 assertEquals(childOverrideConfig1, child1.getRequestedOverrideConfiguration()); 179 assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration()); 180 assertEquals(mergedConfig1, child1.getConfiguration()); 181 182 assertEquals(childOverrideConfig2, child2.getRequestedOverrideConfiguration()); 183 assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration()); 184 assertEquals(mergedConfig2, child2.getConfiguration()); 185 186 // Check configuration on all levels when root parent config is updated. 187 final Configuration rootParentConfig = new Configuration(); 188 rootParentConfig.screenHeightDp = 100; 189 rootParentConfig.orientation = SCREEN_ORIENTATION_REVERSE_PORTRAIT; 190 root.onConfigurationChanged(rootParentConfig); 191 final Configuration mergedRootConfig = new Configuration(rootParentConfig); 192 mergedRootConfig.updateFrom(rootOverrideConfig); 193 194 mergedConfig1.setTo(mergedRootConfig); 195 mergedConfig1.updateFrom(mergedOverrideConfig1); 196 197 mergedConfig2.setTo(mergedConfig1); 198 mergedConfig2.updateFrom(mergedOverrideConfig2); 199 200 assertEquals(rootOverrideConfig, root.getRequestedOverrideConfiguration()); 201 assertEquals(rootOverrideConfig, root.getMergedOverrideConfiguration()); 202 assertEquals(mergedRootConfig, root.getConfiguration()); 203 204 assertEquals(childOverrideConfig1, child1.getRequestedOverrideConfiguration()); 205 assertEquals(mergedOverrideConfig1, child1.getMergedOverrideConfiguration()); 206 assertEquals(mergedConfig1, child1.getConfiguration()); 207 208 assertEquals(childOverrideConfig2, child2.getRequestedOverrideConfiguration()); 209 assertEquals(mergedOverrideConfig2, child2.getMergedOverrideConfiguration()); 210 assertEquals(mergedConfig2, child2.getConfiguration()); 211 } 212 213 @Test testSetAlwaysOnTop()214 public void testSetAlwaysOnTop() { 215 final TestConfigurationContainer root = new TestConfigurationContainer(); 216 final TestConfigurationContainer child1 = root.addChild(); 217 final TestConfigurationContainer child2 = root.addChild(); 218 root.setWindowingMode(WINDOWING_MODE_MULTI_WINDOW); 219 root.setAlwaysOnTop(true); 220 final TestConfigurationContainer child3 = root.addChild(); 221 assertEquals(true, root.isAlwaysOnTop()); 222 assertEquals(false, child1.isAlwaysOnTop()); 223 assertEquals(false, child2.isAlwaysOnTop()); 224 assertEquals(false, child3.isAlwaysOnTop()); 225 } 226 227 @Test testSetWindowingMode()228 public void testSetWindowingMode() { 229 final TestConfigurationContainer root = new TestConfigurationContainer(); 230 root.setWindowingMode(WINDOWING_MODE_UNDEFINED); 231 final TestConfigurationContainer child = root.addChild(); 232 child.setWindowingMode(WINDOWING_MODE_FREEFORM); 233 assertEquals(WINDOWING_MODE_UNDEFINED, root.getWindowingMode()); 234 assertEquals(WINDOWING_MODE_FREEFORM, child.getWindowingMode()); 235 236 root.setWindowingMode(WINDOWING_MODE_FULLSCREEN); 237 assertEquals(WINDOWING_MODE_FULLSCREEN, root.getWindowingMode()); 238 assertEquals(WINDOWING_MODE_FREEFORM, child.getWindowingMode()); 239 } 240 241 @Test testSetActivityType()242 public void testSetActivityType() { 243 final TestConfigurationContainer root = new TestConfigurationContainer(); 244 root.setActivityType(ACTIVITY_TYPE_UNDEFINED); 245 final TestConfigurationContainer child = root.addChild(); 246 child.setActivityType(ACTIVITY_TYPE_STANDARD); 247 assertEquals(ACTIVITY_TYPE_UNDEFINED, root.getActivityType()); 248 assertEquals(ACTIVITY_TYPE_STANDARD, child.getActivityType()); 249 250 boolean gotException = false; 251 try { 252 // Can't change activity type once set. 253 child.setActivityType(ACTIVITY_TYPE_HOME); 254 } catch (IllegalStateException e) { 255 gotException = true; 256 } 257 assertTrue("Can't change activity type once set.", gotException); 258 259 // TODO: Commenting out for now until we figure-out a good way to test these rules that 260 // should only apply to system process. 261 /* 262 gotException = false; 263 try { 264 // Parent can't change child's activity type once set. 265 root.setActivityType(ACTIVITY_TYPE_HOME); 266 } catch (IllegalStateException e) { 267 gotException = true; 268 } 269 assertTrue("Parent can't change activity type once set.", gotException); 270 assertEquals(ACTIVITY_TYPE_HOME, root.getActivityType()); 271 272 final TestConfigurationContainer child2 = new TestConfigurationContainer(); 273 child2.setActivityType(ACTIVITY_TYPE_RECENTS); 274 275 gotException = false; 276 try { 277 // Can't re-parent to a different activity type. 278 root.addChild(child2); 279 } catch (IllegalStateException e) { 280 gotException = true; 281 } 282 assertTrue("Can't re-parent to a different activity type.", gotException); 283 */ 284 285 } 286 287 @Test testRegisterConfigurationChangeListener()288 public void testRegisterConfigurationChangeListener() { 289 final TestConfigurationContainer container = new TestConfigurationContainer(); 290 final TestConfigurationChangeListener listener = new TestConfigurationChangeListener(); 291 final Configuration config = new Configuration(); 292 config.windowConfiguration.setWindowingMode(WINDOWING_MODE_FREEFORM); 293 config.windowConfiguration.setAppBounds(10, 10, 10, 10); 294 container.onRequestedOverrideConfigurationChanged(config); 295 container.registerConfigurationChangeListener(listener); 296 // Assert listener got the current config. of the container after it was registered. 297 assertEquals(config, listener.mOverrideConfiguration); 298 // Assert listener gets changes to override configuration. 299 container.onRequestedOverrideConfigurationChanged(EMPTY); 300 assertEquals(EMPTY, listener.mOverrideConfiguration); 301 } 302 303 @Test testConfigurationConstraints()304 public void testConfigurationConstraints() { 305 // Init root config. 306 final TestConfigurationContainer root = new TestConfigurationContainer(); 307 final Configuration rootOverrideConfig = new Configuration(); 308 rootOverrideConfig.smallestScreenWidthDp = 140; 309 root.onRequestedOverrideConfigurationChanged(rootOverrideConfig); 310 311 // Init child with constraint 312 final TestConfigurationChangeListener listener = new TestConfigurationChangeListener(); 313 final TestConfigurationContainer child1 = root.addConstraintChild(); 314 child1.registerConfigurationChangeListener(listener); 315 final Configuration childOverrideConfig1 = new Configuration(); 316 childOverrideConfig1.orientation = SCREEN_ORIENTATION_LANDSCAPE; 317 child1.onRequestedOverrideConfigurationChanged(childOverrideConfig1); 318 319 assertEquals(SMALLEST_SCREEN_WIDTH_DP_UNDEFINED, 320 child1.getRequestedOverrideConfiguration().smallestScreenWidthDp); 321 assertEquals(100, child1.getConfiguration().smallestScreenWidthDp); 322 assertEquals(100, listener.mOverrideConfiguration.smallestScreenWidthDp); 323 324 // Check configuration on all levels when root override is updated. 325 rootOverrideConfig.smallestScreenWidthDp = 80; 326 root.onRequestedOverrideConfigurationChanged(rootOverrideConfig); 327 328 assertEquals(SMALLEST_SCREEN_WIDTH_DP_UNDEFINED, 329 child1.getRequestedOverrideConfiguration().smallestScreenWidthDp); 330 assertEquals(80, child1.getConfiguration().smallestScreenWidthDp); 331 assertEquals(80, listener.mOverrideConfiguration.smallestScreenWidthDp); 332 333 rootOverrideConfig.smallestScreenWidthDp = 180; 334 root.onRequestedOverrideConfigurationChanged(rootOverrideConfig); 335 336 assertEquals(SMALLEST_SCREEN_WIDTH_DP_UNDEFINED, 337 child1.getRequestedOverrideConfiguration().smallestScreenWidthDp); 338 assertEquals(100, child1.getConfiguration().smallestScreenWidthDp); 339 assertEquals(100, child1.getMergedOverrideConfiguration().smallestScreenWidthDp); 340 assertEquals(100, listener.mOverrideConfiguration.smallestScreenWidthDp); 341 } 342 343 @Test testSetMaxBoundsByHierarchy()344 public void testSetMaxBoundsByHierarchy() { 345 final TestConfigurationContainer root = 346 new TestConfigurationContainer(true /* providesMaxBounds */); 347 final Rect bounds = new Rect(0, 0, 10, 10); 348 final TestConfigurationContainer child = new TestConfigurationContainer(); 349 root.addChild(child); 350 351 root.setBounds(bounds); 352 353 assertEquals(bounds, root.getBounds()); 354 assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds()); 355 assertEquals(bounds, child.getBounds()); 356 assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds()); 357 358 assertEquals(bounds, root.getMaxBounds()); 359 assertEquals(bounds, root.getConfiguration().windowConfiguration.getMaxBounds()); 360 assertEquals(bounds, child.getMaxBounds()); 361 assertEquals(bounds, child.getConfiguration().windowConfiguration.getMaxBounds()); 362 } 363 364 @Test testSetBoundsNotOverrideMaxBounds()365 public void testSetBoundsNotOverrideMaxBounds() { 366 final TestConfigurationContainer root = new TestConfigurationContainer(); 367 final Rect bounds = new Rect(0, 0, 10, 10); 368 final TestConfigurationContainer child = new TestConfigurationContainer(); 369 root.addChild(child); 370 371 root.setBounds(bounds); 372 373 assertEquals(bounds, root.getBounds()); 374 assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds()); 375 assertEquals(bounds, child.getBounds()); 376 assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds()); 377 378 assertTrue(root.getMaxBounds().isEmpty()); 379 assertTrue(root.getConfiguration().windowConfiguration.getMaxBounds().isEmpty()); 380 assertTrue(child.getMaxBounds().isEmpty()); 381 assertTrue(child.getConfiguration().windowConfiguration.getMaxBounds().isEmpty()); 382 } 383 384 @Test testOnRequestedOverrideConfigurationChangedOverrideMaxBounds()385 public void testOnRequestedOverrideConfigurationChangedOverrideMaxBounds() { 386 final TestConfigurationContainer root = 387 new TestConfigurationContainer(true /* providesMaxBounds */); 388 final Rect bounds = new Rect(0, 0, 10, 10); 389 final TestConfigurationContainer child = new TestConfigurationContainer(); 390 root.addChild(child); 391 final Configuration configuration = new Configuration(); 392 configuration.windowConfiguration.setBounds(bounds); 393 394 root.onRequestedOverrideConfigurationChanged(configuration); 395 396 assertEquals(bounds, root.getBounds()); 397 assertEquals(bounds, root.getConfiguration().windowConfiguration.getBounds()); 398 assertEquals(bounds, child.getBounds()); 399 assertEquals(bounds, child.getConfiguration().windowConfiguration.getBounds()); 400 401 assertEquals(bounds, root.getMaxBounds()); 402 assertEquals(bounds, root.getConfiguration().windowConfiguration.getMaxBounds()); 403 assertEquals(bounds, child.getMaxBounds()); 404 assertEquals(bounds, child.getConfiguration().windowConfiguration.getMaxBounds()); 405 } 406 407 408 /** 409 * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed 410 * for testing. 411 */ 412 private class TestConfigurationContainer 413 extends ConfigurationContainer<TestConfigurationContainer> { 414 private List<TestConfigurationContainer> mChildren = new ArrayList<>(); 415 private TestConfigurationContainer mParent; 416 417 private boolean mProvidesMaxBounds = false; 418 TestConfigurationContainer()419 TestConfigurationContainer() {} 420 TestConfigurationContainer(boolean providesMaxBounds)421 TestConfigurationContainer(boolean providesMaxBounds) { 422 mProvidesMaxBounds = providesMaxBounds; 423 } 424 addChild(TestConfigurationContainer childContainer)425 TestConfigurationContainer addChild(TestConfigurationContainer childContainer) { 426 final ConfigurationContainer oldParent = childContainer.getParent(); 427 childContainer.mParent = this; 428 childContainer.onParentChanged(this, oldParent); 429 mChildren.add(childContainer); 430 return childContainer; 431 } 432 addChild()433 TestConfigurationContainer addChild() { 434 return addChild(new TestConfigurationContainer()); 435 } 436 addConstraintChild()437 TestConfigurationContainer addConstraintChild() { 438 return addChild(new TestConfigurationContainerWithConstraints()); 439 } 440 removeChild(TestConfigurationContainer child)441 void removeChild(TestConfigurationContainer child) { 442 final ConfigurationContainer oldParent = child.getParent(); 443 child.mParent = null; 444 child.onParentChanged(null, oldParent); 445 } 446 447 @Override getChildCount()448 protected int getChildCount() { 449 return mChildren.size(); 450 } 451 452 @Override getChildAt(int index)453 protected TestConfigurationContainer getChildAt(int index) { 454 return mChildren.get(index); 455 } 456 457 @Override getParent()458 protected ConfigurationContainer getParent() { 459 return mParent; 460 } 461 462 @Override providesMaxBounds()463 public boolean providesMaxBounds() { 464 return mProvidesMaxBounds; 465 } 466 } 467 468 /** 469 * Contains minimal implementation of {@link ConfigurationContainer}'s abstract behavior needed 470 * for testing. 471 */ 472 private class TestConfigurationContainerWithConstraints 473 extends TestConfigurationContainer { 474 475 @Override resolveOverrideConfiguration(Configuration newParentConfig)476 public void resolveOverrideConfiguration(Configuration newParentConfig) { 477 // Restrict smallestScreenWidthDp to 100 478 getResolvedOverrideConfiguration().setTo(getRequestedOverrideConfiguration()); 479 int smallestScreenWidthDp = 480 getResolvedOverrideConfiguration().smallestScreenWidthDp 481 == SMALLEST_SCREEN_WIDTH_DP_UNDEFINED 482 ? newParentConfig.smallestScreenWidthDp 483 : getResolvedOverrideConfiguration().smallestScreenWidthDp; 484 if (smallestScreenWidthDp != SMALLEST_SCREEN_WIDTH_DP_UNDEFINED) { 485 getResolvedOverrideConfiguration().smallestScreenWidthDp = 486 Math.min(smallestScreenWidthDp, 100); 487 } 488 } 489 } 490 491 private static class TestConfigurationChangeListener implements ConfigurationContainerListener { 492 493 final Configuration mOverrideConfiguration = new Configuration(); 494 495 @Override onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration)496 public void onRequestedOverrideConfigurationChanged(Configuration overrideConfiguration) { 497 mOverrideConfiguration.setTo(overrideConfiguration); 498 } 499 } 500 } 501