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 com.android.server.wm; 18 19 import static android.view.WindowManager.LayoutParams.TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY; 20 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; 21 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD; 22 import static android.view.WindowManager.LayoutParams.TYPE_INPUT_METHOD_DIALOG; 23 import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR; 24 import static android.view.WindowManager.LayoutParams.TYPE_POINTER; 25 import static android.view.WindowManager.LayoutParams.TYPE_PRESENTATION; 26 import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; 27 import static android.view.WindowManager.LayoutParams.TYPE_WALLPAPER; 28 import static android.view.WindowManagerPolicyConstants.APPLICATION_LAYER; 29 import static android.window.DisplayAreaOrganizer.FEATURE_DEFAULT_TASK_CONTAINER; 30 import static android.window.DisplayAreaOrganizer.FEATURE_FULLSCREEN_MAGNIFICATION; 31 import static android.window.DisplayAreaOrganizer.FEATURE_IME_PLACEHOLDER; 32 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED; 33 import static android.window.DisplayAreaOrganizer.FEATURE_ONE_HANDED_BACKGROUND_PANEL; 34 import static android.window.DisplayAreaOrganizer.FEATURE_ROOT; 35 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_FIRST; 36 import static android.window.DisplayAreaOrganizer.FEATURE_VENDOR_LAST; 37 import static android.window.DisplayAreaOrganizer.FEATURE_WINDOWED_MAGNIFICATION; 38 import static android.window.DisplayAreaOrganizer.KEY_ROOT_DISPLAY_AREA_ID; 39 40 import static com.android.server.wm.DisplayArea.Type.ABOVE_TASKS; 41 import static com.android.server.wm.DisplayAreaPolicyBuilder.Feature; 42 43 import static com.google.common.truth.Truth.assertThat; 44 45 import static org.mockito.Mockito.doReturn; 46 import static org.mockito.Mockito.mock; 47 import static org.mockito.Mockito.when; 48 import static org.testng.Assert.assertThrows; 49 50 import static java.util.stream.Collectors.toList; 51 52 import android.content.res.Resources; 53 import android.os.Binder; 54 import android.os.Bundle; 55 import android.os.IBinder; 56 import android.platform.test.annotations.Presubmit; 57 import android.view.SurfaceControl; 58 59 import com.google.android.collect.Lists; 60 61 import org.hamcrest.CustomTypeSafeMatcher; 62 import org.hamcrest.Description; 63 import org.hamcrest.Matcher; 64 import org.junit.Before; 65 import org.junit.Rule; 66 import org.junit.Test; 67 68 import java.util.ArrayList; 69 import java.util.HashMap; 70 import java.util.HashSet; 71 import java.util.List; 72 import java.util.Map; 73 import java.util.Set; 74 import java.util.function.Consumer; 75 import java.util.function.Function; 76 import java.util.stream.Collectors; 77 78 /** 79 * Build/Install/Run: 80 * atest WmTests:DisplayAreaPolicyBuilderTest 81 */ 82 @Presubmit 83 public class DisplayAreaPolicyBuilderTest { 84 85 @Rule 86 public final SystemServicesTestRule mSystemServices = new SystemServicesTestRule(); 87 88 private TestWindowManagerPolicy mPolicy = new TestWindowManagerPolicy(null, null); 89 private WindowManagerService mWms; 90 private RootDisplayArea mRoot; 91 private DisplayArea.Tokens mImeContainer; 92 private DisplayContent mDisplayContent; 93 private TaskDisplayArea mDefaultTaskDisplayArea; 94 private List<TaskDisplayArea> mTaskDisplayAreaList; 95 private RootDisplayArea mGroupRoot1; 96 private RootDisplayArea mGroupRoot2; 97 private TaskDisplayArea mTda1; 98 private TaskDisplayArea mTda2; 99 100 @Before setup()101 public void setup() { 102 mWms = mSystemServices.getWindowManagerService(); 103 mRoot = new SurfacelessDisplayAreaRoot(mWms); 104 mImeContainer = new DisplayArea.Tokens(mWms, ABOVE_TASKS, "ImeContainer"); 105 mDisplayContent = mock(DisplayContent.class); 106 doReturn(true).when(mDisplayContent).isTrusted(); 107 mDisplayContent.isDefaultDisplay = true; 108 mDefaultTaskDisplayArea = new TaskDisplayArea(mDisplayContent, mWms, "Tasks", 109 FEATURE_DEFAULT_TASK_CONTAINER); 110 mTaskDisplayAreaList = new ArrayList<>(); 111 mTaskDisplayAreaList.add(mDefaultTaskDisplayArea); 112 mGroupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1", FEATURE_VENDOR_FIRST + 1); 113 mGroupRoot2 = new SurfacelessDisplayAreaRoot(mWms, "group2", FEATURE_VENDOR_FIRST + 2); 114 mTda1 = new TaskDisplayArea(mDisplayContent, mWms, "tda1", FEATURE_VENDOR_FIRST + 3); 115 mTda2 = new TaskDisplayArea(mDisplayContent, mWms, "tda2", FEATURE_VENDOR_FIRST + 4); 116 } 117 118 @Test testBuilder()119 public void testBuilder() { 120 final Feature foo; 121 final Feature bar; 122 DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = 123 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 124 .addFeature(foo = new Feature.Builder(mPolicy, "Foo", 125 FEATURE_VENDOR_FIRST) 126 .upTo(TYPE_STATUS_BAR) 127 .and(TYPE_NAVIGATION_BAR) 128 .build()) 129 .addFeature(bar = new Feature.Builder(mPolicy, "Bar", 130 FEATURE_VENDOR_FIRST + 1) 131 .all() 132 .except(TYPE_STATUS_BAR) 133 .build()) 134 .setImeContainer(mImeContainer) 135 .setTaskDisplayAreas(mTaskDisplayAreaList); 136 DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() 137 .setRootHierarchy(rootHierarchy) 138 .build(mWms); 139 140 assertThat(policy.getDisplayAreas(foo.getId())).isNotEmpty(); 141 assertThat(policy.getDisplayAreas(bar.getId())).isNotEmpty(); 142 143 Matcher<WindowContainer> fooDescendantMatcher = descendantOfOneOf( 144 policy.getDisplayAreas(foo.getId())); 145 Matcher<WindowContainer> barDescendantMatcher = descendantOfOneOf( 146 policy.getDisplayAreas(bar.getId())); 147 148 // There is a DA of TYPE_STATUS_BAR below foo, but not below bar 149 assertThat(fooDescendantMatcher.matches( 150 policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)))).isTrue(); 151 assertThat(barDescendantMatcher.matches( 152 policy.findAreaForToken(tokenOfType(TYPE_STATUS_BAR)))).isFalse(); 153 154 // The TDA is below both foo and bar. 155 assertThat(fooDescendantMatcher.matches(mDefaultTaskDisplayArea)).isTrue(); 156 assertThat(barDescendantMatcher.matches(mDefaultTaskDisplayArea)).isTrue(); 157 158 // The IME is below both foo and bar. 159 assertThat(fooDescendantMatcher.matches(mImeContainer)).isTrue(); 160 assertThat(barDescendantMatcher.matches(mImeContainer)).isTrue(); 161 assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD))) 162 .isEqualTo(mImeContainer); 163 assertThat(policy.findAreaForToken(tokenOfType(TYPE_INPUT_METHOD_DIALOG))) 164 .isEqualTo(mImeContainer); 165 166 List<DisplayArea<?>> actualOrder = collectLeafAreas(mRoot); 167 Map<DisplayArea<?>, Set<Integer>> zSets = calculateZSets(policy, mImeContainer, 168 mDefaultTaskDisplayArea); 169 actualOrder = actualOrder.stream().filter(zSets::containsKey).collect(toList()); 170 171 Map<DisplayArea<?>, Integer> expectedByMinLayer = mapValues(zSets, 172 v -> v.stream().min(Integer::compareTo).get()); 173 Map<DisplayArea<?>, Integer> expectedByMaxLayer = mapValues(zSets, 174 v -> v.stream().max(Integer::compareTo).get()); 175 176 // Make sure the DAs' order is the same as their layer order. 177 assertMatchLayerOrder(actualOrder, expectedByMinLayer); 178 assertMatchLayerOrder(actualOrder, expectedByMaxLayer); 179 } 180 181 @Test testBuilder_defaultPolicy_hasOneHandedFeature()182 public void testBuilder_defaultPolicy_hasOneHandedFeature() { 183 final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources( 184 resourcesWithProvider("")); 185 final DisplayAreaPolicyBuilder.Result defaultPolicy = 186 (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent, 187 mRoot, mImeContainer); 188 if (mDisplayContent.isDefaultDisplay) { 189 final List<Feature> features = defaultPolicy.getFeatures(); 190 boolean hasOneHandedFeature = false; 191 for (Feature feature : features) { 192 hasOneHandedFeature |= feature.getId() == FEATURE_ONE_HANDED; 193 } 194 195 assertThat(hasOneHandedFeature).isTrue(); 196 } 197 } 198 199 @Test testBuilder_defaultPolicy_hasOneHandedBackgroundFeature()200 public void testBuilder_defaultPolicy_hasOneHandedBackgroundFeature() { 201 final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources( 202 resourcesWithProvider("")); 203 final DisplayAreaPolicyBuilder.Result defaultPolicy = 204 (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent, 205 mRoot, mImeContainer); 206 if (mDisplayContent.isDefaultDisplay) { 207 final List<Feature> features = defaultPolicy.getFeatures(); 208 boolean hasOneHandedBackgroundFeature = false; 209 for (Feature feature : features) { 210 hasOneHandedBackgroundFeature |= 211 feature.getId() == FEATURE_ONE_HANDED_BACKGROUND_PANEL; 212 } 213 214 assertThat(hasOneHandedBackgroundFeature).isTrue(); 215 } 216 } 217 218 @Test testBuilder_defaultPolicy_hasWindowedMagnificationFeature()219 public void testBuilder_defaultPolicy_hasWindowedMagnificationFeature() { 220 final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources( 221 resourcesWithProvider("")); 222 final DisplayAreaPolicyBuilder.Result defaultPolicy = 223 (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent, 224 mRoot, mImeContainer); 225 final List<Feature> features = defaultPolicy.getFeatures(); 226 boolean hasWindowedMagnificationFeature = false; 227 for (Feature feature : features) { 228 hasWindowedMagnificationFeature |= feature.getId() == FEATURE_WINDOWED_MAGNIFICATION; 229 } 230 231 assertThat(hasWindowedMagnificationFeature).isTrue(); 232 } 233 234 @Test testBuilder_defaultPolicy_hasFullscreenMagnificationFeature()235 public void testBuilder_defaultPolicy_hasFullscreenMagnificationFeature() { 236 final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources( 237 resourcesWithProvider("")); 238 final DisplayAreaPolicyBuilder.Result defaultPolicy = 239 (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent, 240 mRoot, mImeContainer); 241 final List<Feature> features = defaultPolicy.getFeatures(); 242 boolean hasFullscreenMagnificationFeature = false; 243 for (Feature feature : features) { 244 hasFullscreenMagnificationFeature |= 245 feature.getId() == FEATURE_FULLSCREEN_MAGNIFICATION; 246 } 247 248 assertThat(hasFullscreenMagnificationFeature).isTrue(); 249 } 250 251 @Test testBuilder_defaultPolicy_hasImePlaceholderFeature()252 public void testBuilder_defaultPolicy_hasImePlaceholderFeature() { 253 final DisplayAreaPolicy.Provider defaultProvider = DisplayAreaPolicy.Provider.fromResources( 254 resourcesWithProvider("")); 255 final DisplayAreaPolicyBuilder.Result defaultPolicy = 256 (DisplayAreaPolicyBuilder.Result) defaultProvider.instantiate(mWms, mDisplayContent, 257 mRoot, mImeContainer); 258 final List<Feature> features = defaultPolicy.getFeatures(); 259 boolean hasImePlaceholderFeature = false; 260 for (Feature feature : features) { 261 hasImePlaceholderFeature |= feature.getId() == FEATURE_IME_PLACEHOLDER; 262 } 263 264 assertThat(hasImePlaceholderFeature).isTrue(); 265 } 266 267 @Test testBuilder_createCustomizedDisplayAreaForFeature()268 public void testBuilder_createCustomizedDisplayAreaForFeature() { 269 final Feature dimmable; 270 final Feature other; 271 DisplayAreaPolicyBuilder.HierarchyBuilder rootHierarchy = 272 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 273 .addFeature(dimmable = new Feature.Builder(mPolicy, "Dimmable", 274 FEATURE_VENDOR_FIRST) 275 .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) 276 .except(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) 277 .setNewDisplayAreaSupplier(DisplayArea.Dimmable::new) 278 .build()) 279 .addFeature(other = new Feature.Builder(mPolicy, "Other", 280 FEATURE_VENDOR_FIRST + 1) 281 .all() 282 .build()) 283 .setImeContainer(mImeContainer) 284 .setTaskDisplayAreas(mTaskDisplayAreaList); 285 DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() 286 .setRootHierarchy(rootHierarchy) 287 .build(mWms); 288 289 List<DisplayArea<? extends WindowContainer>> dimmableDAs = 290 policy.getDisplayAreas(dimmable.getId()); 291 List<DisplayArea<? extends WindowContainer>> otherDAs = 292 policy.getDisplayAreas(other.getId()); 293 assertThat(dimmableDAs).hasSize(1); 294 assertThat(dimmableDAs.get(0)).isInstanceOf(DisplayArea.Dimmable.class); 295 for (DisplayArea otherDA : otherDAs) { 296 assertThat(otherDA).isNotInstanceOf(DisplayArea.Dimmable.class); 297 } 298 } 299 300 @Test testBuilder_singleRoot_validateSettings()301 public void testBuilder_singleRoot_validateSettings() { 302 final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder(); 303 304 // Root must be set. 305 assertThrows(IllegalStateException.class, () -> builder.build(mWms)); 306 307 // IME must be set. 308 builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 309 .setTaskDisplayAreas(mTaskDisplayAreaList)); 310 311 assertThrows(IllegalStateException.class, () -> builder.build(mWms)); 312 313 // Default TaskDisplayArea must be set. 314 builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 315 .setImeContainer(mImeContainer) 316 .setTaskDisplayAreas(Lists.newArrayList( 317 new TaskDisplayArea(mDisplayContent, mWms, "testTda", 318 FEATURE_VENDOR_FIRST + 1)))); 319 320 assertThrows(IllegalStateException.class, () -> builder.build(mWms)); 321 322 // No exception 323 builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 324 .setImeContainer(mImeContainer) 325 .setTaskDisplayAreas(mTaskDisplayAreaList)); 326 327 builder.build(mWms); 328 } 329 330 @Test testBuilder_displayAreaGroup_validateSettings()331 public void testBuilder_displayAreaGroup_validateSettings() { 332 final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder(); 333 334 // IME must be set to one of the roots. 335 builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); 336 builder1.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 337 mGroupRoot1) 338 .setTaskDisplayAreas(mTaskDisplayAreaList)); 339 340 assertThrows(IllegalStateException.class, () -> builder1.build(mWms)); 341 342 // Default TaskDisplayArea must be set. 343 final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); 344 builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); 345 builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 346 mGroupRoot1) 347 .setImeContainer(mImeContainer) 348 .setTaskDisplayAreas(Lists.newArrayList(mTda1))); 349 350 assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); 351 352 // Each DisplayAreaGroup must have at least one TaskDisplayArea. 353 final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder(); 354 builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); 355 builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 356 mGroupRoot1) 357 .setImeContainer(mImeContainer) 358 .setTaskDisplayAreas(mTaskDisplayAreaList)); 359 builder3.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 360 mGroupRoot2)); 361 362 assertThrows(IllegalStateException.class, () -> builder3.build(mWms)); 363 364 // No exception 365 final DisplayAreaPolicyBuilder builder4 = new DisplayAreaPolicyBuilder(); 366 builder4.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot)); 367 builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 368 mGroupRoot1) 369 .setImeContainer(mImeContainer) 370 .setTaskDisplayAreas(mTaskDisplayAreaList)); 371 builder4.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 372 mGroupRoot2) 373 .setTaskDisplayAreas(Lists.newArrayList(mTda1))); 374 375 builder4.build(mWms); 376 } 377 378 @Test testBuilder_rootHasUniqueId()379 public void testBuilder_rootHasUniqueId() { 380 // Root must have different id from all roots. 381 final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder(); 382 builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 383 .setImeContainer(mImeContainer) 384 .setTaskDisplayAreas(mTaskDisplayAreaList)); 385 final RootDisplayArea groupRoot1 = new SurfacelessDisplayAreaRoot(mWms, "group1", 386 mRoot.mFeatureId); 387 builder1.addDisplayAreaGroupHierarchy( 388 new DisplayAreaPolicyBuilder.HierarchyBuilder(groupRoot1) 389 .setTaskDisplayAreas(Lists.newArrayList(mTda1))); 390 391 assertThrows(IllegalStateException.class, () -> builder1.build(mWms)); 392 393 // Root must have different id from all TDAs. 394 final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); 395 builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 396 .setImeContainer(mImeContainer) 397 .setTaskDisplayAreas(Lists.newArrayList( 398 mDefaultTaskDisplayArea, 399 new TaskDisplayArea(mDisplayContent, mWms, "testTda", 400 mRoot.mFeatureId)))); 401 402 assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); 403 404 // Root must have different id from all features. 405 final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder(); 406 builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 407 .setImeContainer(mImeContainer) 408 .setTaskDisplayAreas(mTaskDisplayAreaList) 409 .addFeature(new Feature.Builder(mPolicy, "testFeature", mRoot.mFeatureId) 410 .all() 411 .build())); 412 413 assertThrows(IllegalStateException.class, () -> builder3.build(mWms)); 414 } 415 416 @Test testBuilder_taskDisplayAreaHasUniqueId()417 public void testBuilder_taskDisplayAreaHasUniqueId() { 418 // TDA must have different id from all TDAs. 419 final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder(); 420 final List<TaskDisplayArea> tdaList = Lists.newArrayList( 421 mDefaultTaskDisplayArea, 422 mTda1, 423 new TaskDisplayArea(mDisplayContent, mWms, "tda2", mTda1.mFeatureId)); 424 builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 425 .setImeContainer(mImeContainer) 426 .setTaskDisplayAreas(tdaList)); 427 428 assertThrows(IllegalStateException.class, () -> builder.build(mWms)); 429 430 // TDA must have different id from all features. 431 final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); 432 builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 433 .setImeContainer(mImeContainer) 434 .setTaskDisplayAreas(Lists.newArrayList( 435 mDefaultTaskDisplayArea, 436 mTda1)) 437 .addFeature(new Feature.Builder(mPolicy, "testFeature", mTda1.mFeatureId) 438 .all() 439 .build())); 440 441 assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); 442 } 443 444 @Test testBuilder_featureHasUniqueId()445 public void testBuilder_featureHasUniqueId() { 446 // Feature must have different id from features below the same root. 447 final DisplayAreaPolicyBuilder builder = new DisplayAreaPolicyBuilder(); 448 builder.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 449 .setImeContainer(mImeContainer) 450 .setTaskDisplayAreas(mTaskDisplayAreaList) 451 .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10) 452 .all() 453 .build()) 454 .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10) 455 .upTo(TYPE_ACCESSIBILITY_MAGNIFICATION_OVERLAY) 456 .build())); 457 458 assertThrows(IllegalStateException.class, () -> builder.build(mWms)); 459 460 // Features below different root can have the same id. 461 final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); 462 builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 463 .setImeContainer(mImeContainer) 464 .setTaskDisplayAreas(mTaskDisplayAreaList) 465 .addFeature(new Feature.Builder(mPolicy, "feature1", FEATURE_VENDOR_FIRST + 10) 466 .all() 467 .build())); 468 builder2.addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 469 mGroupRoot1) 470 .setTaskDisplayAreas(Lists.newArrayList(mTda1)) 471 .addFeature(new Feature.Builder(mPolicy, "feature2", FEATURE_VENDOR_FIRST + 10) 472 .all() 473 .build())); 474 475 builder2.build(mWms); 476 } 477 478 @Test testBuilder_idsNotGreaterThanFeatureVendorLast()479 public void testBuilder_idsNotGreaterThanFeatureVendorLast() { 480 // Root id should not be greater than FEATURE_VENDOR_LAST. 481 final DisplayAreaPolicyBuilder builder1 = new DisplayAreaPolicyBuilder(); 482 final RootDisplayArea root = new SurfacelessDisplayAreaRoot(mWms, "testRoot", 483 FEATURE_VENDOR_LAST + 1); 484 builder1.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root) 485 .setImeContainer(mImeContainer) 486 .setTaskDisplayAreas(mTaskDisplayAreaList)); 487 488 assertThrows(IllegalStateException.class, () -> builder1.build(mWms)); 489 490 // TDA id should not be greater than FEATURE_VENDOR_LAST. 491 final DisplayAreaPolicyBuilder builder2 = new DisplayAreaPolicyBuilder(); 492 builder2.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(root) 493 .setImeContainer(mImeContainer) 494 .setTaskDisplayAreas(Lists.newArrayList( 495 mDefaultTaskDisplayArea, 496 new TaskDisplayArea(mDisplayContent, mWms, "testTda", 497 FEATURE_VENDOR_LAST + 1)))); 498 499 assertThrows(IllegalStateException.class, () -> builder2.build(mWms)); 500 501 // Feature id should not be greater than FEATURE_VENDOR_LAST. 502 final DisplayAreaPolicyBuilder builder3 = new DisplayAreaPolicyBuilder(); 503 builder3.setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 504 .setImeContainer(mImeContainer) 505 .setTaskDisplayAreas(mTaskDisplayAreaList) 506 .addFeature(new Feature.Builder(mPolicy, "testFeature", FEATURE_VENDOR_LAST + 1) 507 .all() 508 .build())); 509 510 assertThrows(IllegalStateException.class, () -> builder3.build(mWms)); 511 } 512 513 @Test testBuilder_displayAreaGroup_attachDisplayAreas()514 public void testBuilder_displayAreaGroup_attachDisplayAreas() { 515 final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() 516 .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 517 .setTaskDisplayAreas(mTaskDisplayAreaList)) 518 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 519 mGroupRoot1) 520 .setImeContainer(mImeContainer) 521 .setTaskDisplayAreas(Lists.newArrayList(mTda1))) 522 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 523 mGroupRoot2) 524 .setTaskDisplayAreas(Lists.newArrayList(mTda2))) 525 .build(mWms); 526 527 assertThat(mDefaultTaskDisplayArea.isDescendantOf(mRoot)).isTrue(); 528 assertThat(mGroupRoot1.isDescendantOf(mRoot)).isTrue(); 529 assertThat(mGroupRoot2.isDescendantOf(mRoot)).isTrue(); 530 assertThat(mImeContainer.isDescendantOf(mGroupRoot1)).isTrue(); 531 assertThat(mTda1.isDescendantOf(mGroupRoot1)).isTrue(); 532 assertThat(mTda2.isDescendantOf(mGroupRoot2)).isTrue(); 533 assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot1)).isTrue(); 534 assertThat(isSibling(mDefaultTaskDisplayArea, mGroupRoot2)).isTrue(); 535 } 536 537 @Test testBuilder_displayAreaGroup_createFeatureOnGroup()538 public void testBuilder_displayAreaGroup_createFeatureOnGroup() { 539 final Feature feature1 = new Feature.Builder(mWms.mPolicy, "feature1", 540 FEATURE_VENDOR_FIRST + 5) 541 .all() 542 .except(TYPE_STATUS_BAR) 543 .build(); 544 final Feature feature2 = new Feature.Builder(mWms.mPolicy, "feature2", 545 FEATURE_VENDOR_FIRST + 6) 546 .upTo(TYPE_STATUS_BAR) 547 .and(TYPE_NAVIGATION_BAR) 548 .build(); 549 final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() 550 .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 551 .setTaskDisplayAreas(mTaskDisplayAreaList)) 552 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 553 mGroupRoot1) 554 .setImeContainer(mImeContainer) 555 .setTaskDisplayAreas(Lists.newArrayList(mTda1)) 556 .addFeature(feature1)) 557 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 558 mGroupRoot2) 559 .setTaskDisplayAreas(Lists.newArrayList(mTda2)) 560 .addFeature(feature2)) 561 .build(mWms); 562 563 List<DisplayArea<? extends WindowContainer>> feature1DAs = 564 policy.getDisplayAreas(feature1.getId()); 565 List<DisplayArea<? extends WindowContainer>> feature2DAs = 566 policy.getDisplayAreas(feature2.getId()); 567 for (DisplayArea<? extends WindowContainer> da : feature1DAs) { 568 assertThat(da.isDescendantOf(mGroupRoot1)).isTrue(); 569 } 570 for (DisplayArea<? extends WindowContainer> da : feature2DAs) { 571 assertThat(da.isDescendantOf(mGroupRoot2)).isTrue(); 572 } 573 } 574 575 @Test testBuilder_addWindow_selectContainerForWindowFunc_defaultFunc()576 public void testBuilder_addWindow_selectContainerForWindowFunc_defaultFunc() { 577 final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() 578 .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 579 .setTaskDisplayAreas(mTaskDisplayAreaList)) 580 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 581 mGroupRoot1) 582 .setImeContainer(mImeContainer) 583 .setTaskDisplayAreas(Lists.newArrayList(mTda1))) 584 .addDisplayAreaGroupHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder( 585 mGroupRoot2) 586 .setTaskDisplayAreas(Lists.newArrayList(mTda2))) 587 .build(mWms); 588 589 final WindowToken token = new WindowToken.Builder(mWms, mock(IBinder.class), 590 TYPE_STATUS_BAR) 591 .setDisplayContent(mDisplayContent) 592 .setPersistOnEmpty(true) 593 .setOwnerCanManageAppTokens(true) 594 .build(); 595 596 policy.addWindow(token); 597 598 // By default, window are always added to the root. 599 assertThat(token.isDescendantOf(mRoot)).isTrue(); 600 assertThat(token.isDescendantOf(mGroupRoot1)).isFalse(); 601 assertThat(token.isDescendantOf(mGroupRoot2)).isFalse(); 602 603 // When the window has options for target root id, attach it to the target root. 604 final Bundle options = new Bundle(); 605 options.putInt(KEY_ROOT_DISPLAY_AREA_ID, mGroupRoot2.mFeatureId); 606 final WindowToken token2 = new WindowToken.Builder(mWms, mock(IBinder.class), 607 TYPE_STATUS_BAR) 608 .setDisplayContent(mDisplayContent) 609 .setPersistOnEmpty(true) 610 .setOwnerCanManageAppTokens(true) 611 .setOptions(options) 612 .build(); 613 policy.addWindow(token2); 614 615 assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue(); 616 } 617 618 @Test testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnType()619 public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnType() { 620 final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 = 621 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot1) 622 .setImeContainer(mImeContainer) 623 .setTaskDisplayAreas(Lists.newArrayList(mTda1)); 624 final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 = 625 new DisplayAreaPolicyBuilder.HierarchyBuilder(mGroupRoot2) 626 .setTaskDisplayAreas(Lists.newArrayList(mTda2)); 627 final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() 628 .setRootHierarchy(new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 629 .setTaskDisplayAreas(mTaskDisplayAreaList)) 630 .addDisplayAreaGroupHierarchy(hierarchy1) 631 .addDisplayAreaGroupHierarchy(hierarchy2) 632 .setSelectRootForWindowFunc((type, options) -> { 633 if (type == TYPE_STATUS_BAR) { 634 return mGroupRoot1; 635 } 636 return mGroupRoot2; 637 }) 638 .build(mWms); 639 640 final WindowToken token1 = new WindowToken.Builder(mWms, mock(IBinder.class), 641 TYPE_STATUS_BAR) 642 .setDisplayContent(mDisplayContent) 643 .setPersistOnEmpty(true) 644 .setOwnerCanManageAppTokens(true) 645 .build(); 646 final WindowToken token2 = new WindowToken.Builder(mWms, mock(IBinder.class), 647 TYPE_WALLPAPER) 648 .setDisplayContent(mDisplayContent) 649 .setPersistOnEmpty(true) 650 .setOwnerCanManageAppTokens(true) 651 .build(); 652 policy.addWindow(token1); 653 policy.addWindow(token2); 654 655 assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue(); 656 assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue(); 657 } 658 659 @Test testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnOptions()660 public void testBuilder_addWindow_selectContainerForWindowFunc_selectBasedOnOptions() { 661 final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy0 = 662 new DisplayAreaPolicyBuilder.HierarchyBuilder(mRoot) 663 .setTaskDisplayAreas(mTaskDisplayAreaList); 664 final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy1 = 665 new DisplayAreaPolicyBuilder.HierarchyBuilder( 666 mGroupRoot1) 667 .setImeContainer(mImeContainer) 668 .setTaskDisplayAreas(Lists.newArrayList(mTda1)); 669 final DisplayAreaPolicyBuilder.HierarchyBuilder hierarchy2 = 670 new DisplayAreaPolicyBuilder.HierarchyBuilder( 671 mGroupRoot2) 672 .setTaskDisplayAreas(Lists.newArrayList(mTda2)); 673 final DisplayAreaPolicyBuilder.Result policy = new DisplayAreaPolicyBuilder() 674 .setRootHierarchy(hierarchy0) 675 .addDisplayAreaGroupHierarchy(hierarchy1) 676 .addDisplayAreaGroupHierarchy(hierarchy2) 677 .setSelectRootForWindowFunc((token, options) -> { 678 if (options == null) { 679 return mRoot; 680 } 681 if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot1.mFeatureId) { 682 return mGroupRoot1; 683 } 684 if (options.getInt("HIERARCHY_ROOT_ID") == mGroupRoot2.mFeatureId) { 685 return mGroupRoot2; 686 } 687 return mRoot; 688 }) 689 .build(mWms); 690 691 final Bundle options1 = new Bundle(); 692 options1.putInt("HIERARCHY_ROOT_ID", mGroupRoot1.mFeatureId); 693 final Bundle options2 = new Bundle(); 694 options2.putInt("HIERARCHY_ROOT_ID", mGroupRoot2.mFeatureId); 695 final WindowToken token0 = new WindowToken.Builder(mWms, mock(IBinder.class), 696 TYPE_STATUS_BAR) 697 .setDisplayContent(mDisplayContent) 698 .setPersistOnEmpty(true) 699 .setOwnerCanManageAppTokens(true) 700 .build(); 701 final WindowToken token1 = new WindowToken.Builder(mWms, mock(IBinder.class), 702 TYPE_STATUS_BAR) 703 .setDisplayContent(mDisplayContent) 704 .setPersistOnEmpty(true) 705 .setOwnerCanManageAppTokens(true) 706 .setOptions(options1) 707 .build(); 708 final WindowToken token2 = new WindowToken.Builder(mWms, mock(IBinder.class), 709 TYPE_STATUS_BAR) 710 .setDisplayContent(mDisplayContent) 711 .setPersistOnEmpty(true) 712 .setOwnerCanManageAppTokens(true) 713 .setOptions(options2) 714 .build(); 715 716 policy.addWindow(token0); 717 policy.addWindow(token1); 718 policy.addWindow(token2); 719 720 assertThat(token0.isDescendantOf(mRoot)).isTrue(); 721 assertThat(token1.isDescendantOf(mGroupRoot1)).isTrue(); 722 assertThat(token2.isDescendantOf(mGroupRoot2)).isTrue(); 723 } 724 725 @Test testFeatureNotThrowArrayIndexOutOfBoundsException()726 public void testFeatureNotThrowArrayIndexOutOfBoundsException() { 727 final Feature feature1 = new Feature.Builder(mWms.mPolicy, "feature1", 728 FEATURE_VENDOR_FIRST + 5) 729 .all() 730 .except(TYPE_POINTER) 731 .build(); 732 } 733 resourcesWithProvider(String provider)734 private static Resources resourcesWithProvider(String provider) { 735 Resources mock = mock(Resources.class); 736 when(mock.getString( 737 com.android.internal.R.string.config_deviceSpecificDisplayAreaPolicyProvider)) 738 .thenReturn(provider); 739 return mock; 740 } 741 mapValues(Map<K, V> zSets, Function<V, R> f)742 private <K, V, R> Map<K, R> mapValues(Map<K, V> zSets, Function<V, R> f) { 743 return zSets.entrySet().stream().collect(Collectors.toMap( 744 Map.Entry::getKey, 745 e -> f.apply(e.getValue()))); 746 } 747 collectLeafAreas(DisplayArea<?> root)748 private List<DisplayArea<?>> collectLeafAreas(DisplayArea<?> root) { 749 ArrayList<DisplayArea<?>> leafs = new ArrayList<>(); 750 traverseLeafAreas(root, leafs::add); 751 return leafs; 752 } 753 calculateZSets( DisplayAreaPolicyBuilder.Result policy, DisplayArea.Tokens ime, TaskDisplayArea taskDisplayArea)754 private Map<DisplayArea<?>, Set<Integer>> calculateZSets( 755 DisplayAreaPolicyBuilder.Result policy, 756 DisplayArea.Tokens ime, 757 TaskDisplayArea taskDisplayArea) { 758 Map<DisplayArea<?>, Set<Integer>> zSets = new HashMap<>(); 759 int[] types = {TYPE_STATUS_BAR, TYPE_NAVIGATION_BAR, TYPE_PRESENTATION, 760 TYPE_APPLICATION_OVERLAY}; 761 for (int type : types) { 762 WindowToken token = tokenOfType(type); 763 recordLayer(policy.findAreaForToken(token), token.getWindowLayerFromType(), zSets); 764 } 765 recordLayer(taskDisplayArea, APPLICATION_LAYER, zSets); 766 recordLayer(ime, mPolicy.getWindowLayerFromTypeLw(TYPE_INPUT_METHOD), zSets); 767 return zSets; 768 } 769 recordLayer(DisplayArea<?> area, int layer, Map<DisplayArea<?>, Set<Integer>> zSets)770 private void recordLayer(DisplayArea<?> area, int layer, 771 Map<DisplayArea<?>, Set<Integer>> zSets) { 772 zSets.computeIfAbsent(area, k -> new HashSet<>()).add(layer); 773 } 774 descendantOfOneOf(List<? extends WindowContainer> expected)775 private Matcher<WindowContainer> descendantOfOneOf(List<? extends WindowContainer> expected) { 776 return new CustomTypeSafeMatcher<WindowContainer>("descendant of one of " + expected) { 777 @Override 778 protected boolean matchesSafely(WindowContainer actual) { 779 for (WindowContainer expected : expected) { 780 WindowContainer candidate = actual; 781 while (candidate != null && candidate.getParent() != candidate) { 782 if (candidate.getParent() == expected) { 783 return true; 784 } 785 candidate = candidate.getParent(); 786 } 787 } 788 return false; 789 } 790 791 @Override 792 protected void describeMismatchSafely(WindowContainer item, 793 Description description) { 794 description.appendText("was ").appendValue(item); 795 while (item != null && item.getParent() != item) { 796 item = item.getParent(); 797 description.appendText(", child of ").appendValue(item); 798 } 799 } 800 }; 801 } 802 803 private boolean isSibling(WindowContainer da1, WindowContainer da2) { 804 return da1.getParent() != null && da1.getParent() == da2.getParent(); 805 } 806 807 private WindowToken tokenOfType(int type) { 808 return new WindowToken.Builder(mWms, new Binder(), type) 809 .setDisplayContent(mDisplayContent).build(); 810 } 811 812 private static void assertMatchLayerOrder(List<DisplayArea<?>> actualOrder, 813 Map<DisplayArea<?>, Integer> areaToLayerMap) { 814 for (int i = 0; i < actualOrder.size() - 1; i++) { 815 DisplayArea<?> curr = actualOrder.get(i); 816 DisplayArea<?> next = actualOrder.get(i + 1); 817 assertThat(areaToLayerMap.get(curr)).isLessThan(areaToLayerMap.get(next)); 818 } 819 } 820 821 private static void traverseLeafAreas(DisplayArea<?> root, Consumer<DisplayArea<?>> consumer) { 822 boolean leaf = true; 823 for (int i = 0; i < root.getChildCount(); i++) { 824 WindowContainer child = root.getChildAt(i); 825 if (child instanceof DisplayArea<?>) { 826 traverseLeafAreas((DisplayArea<?>) child, consumer); 827 leaf = false; 828 } 829 } 830 if (leaf) { 831 consumer.accept(root); 832 } 833 } 834 835 static class SurfacelessDisplayAreaRoot extends RootDisplayArea { 836 837 SurfacelessDisplayAreaRoot(WindowManagerService wms) { 838 this(wms, "SurfacelessDisplayAreaRoot", FEATURE_ROOT); 839 } 840 841 SurfacelessDisplayAreaRoot(WindowManagerService wms, String name, int featureId) { 842 super(wms, name, featureId); 843 } 844 845 @Override 846 SurfaceControl.Builder makeChildSurface(WindowContainer child) { 847 return new MockSurfaceControlBuilder(); 848 } 849 } 850 851 } 852