1/* 2 * Copyright (C) 2024 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 17import {Rect} from 'common/geometry/rect'; 18import {Region} from 'common/geometry/region'; 19import {IDENTITY_MATRIX} from 'common/geometry/transform_matrix'; 20import { 21 Transform, 22 TransformTypeFlags, 23} from 'parsers/surface_flinger/transform_utils'; 24import {android} from 'protos/surfaceflinger/udc/static'; 25import {HierarchyTreeBuilder} from 'test/unit/hierarchy_tree_builder'; 26import {TraceRect} from 'trace/trace_rect'; 27import {TraceRectBuilder} from 'trace/trace_rect_builder'; 28import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 29import {InputConfig, RectsComputation} from './rects_computation'; 30import {ZOrderPathsComputation} from './z_order_paths_computation'; 31 32describe('SurfaceFlinger RectsComputation', () => { 33 const rotationTransform = new Transform( 34 TransformTypeFlags.ROT_90_VAL, 35 IDENTITY_MATRIX, 36 ); 37 let computation: RectsComputation; 38 39 beforeEach(() => { 40 computation = new RectsComputation(); 41 }); 42 43 it('throws error if root not set', () => { 44 expect(() => computation.executeInPlace()).toThrowError(); 45 }); 46 47 it('throws error if visibility not already computed', () => { 48 const hierarchyRoot = new HierarchyTreeBuilder() 49 .setId('LayerTraceEntry') 50 .setName('root') 51 .setChildren([ 52 { 53 id: 1, 54 name: 'layer1', 55 properties: { 56 id: 1, 57 name: 'layer1', 58 cornerRadius: 0, 59 layerStack: 0, 60 bounds: {left: 0, top: 0, right: 1, bottom: 1}, 61 screenBounds: {left: 0, top: 0, right: 1, bottom: 1}, 62 z: 0, 63 transform: Transform.EMPTY, 64 } as android.surfaceflinger.ILayerProto, 65 }, 66 ]) 67 .build(); 68 expect(() => 69 computation.setRoot(hierarchyRoot).executeInPlace(), 70 ).toThrowError(); 71 }); 72 73 it('makes layer rects according to z order', () => { 74 const hierarchyRoot = new HierarchyTreeBuilder() 75 .setId('LayerTraceEntry') 76 .setName('root') 77 .setChildren([ 78 { 79 id: 1, 80 name: 'layer1', 81 properties: { 82 id: 1, 83 name: 'layer1', 84 cornerRadius: 0, 85 layerStack: 0, 86 bounds: {left: 0, top: 0, right: 1, bottom: 1}, 87 screenBounds: {left: 0, top: 0, right: 1, bottom: 1}, 88 z: 0, 89 isComputedVisible: true, 90 transform: Transform.EMPTY, 91 } as android.surfaceflinger.ILayerProto, 92 children: [ 93 { 94 id: 2, 95 name: 'layer2', 96 properties: { 97 id: 2, 98 name: 'layer2', 99 cornerRadius: 2, 100 layerStack: 0, 101 bounds: {left: 0, top: 0, right: 2, bottom: 2}, 102 screenBounds: {left: 0, top: 0, right: 2, bottom: 2}, 103 z: 1, 104 occludedBy: [1], 105 isComputedVisible: false, 106 transform: Transform.EMPTY, 107 } as android.surfaceflinger.ILayerProto, 108 }, 109 ], 110 }, 111 { 112 id: 4, 113 name: 'layerRelativeZ', 114 properties: { 115 id: 4, 116 name: 'layerRelativeZ', 117 cornerRadius: 0, 118 layerStack: 0, 119 bounds: {left: 0, top: 0, right: 5, bottom: 5}, 120 screenBounds: {left: 0, top: 0, right: 5, bottom: 5}, 121 z: 2, 122 zOrderRelativeOf: 1, 123 isComputedVisible: true, 124 color: {r: 0, g: 0, b: 0, a: 1}, 125 transform: Transform.EMPTY, 126 } as android.surfaceflinger.ILayerProto, 127 }, 128 ]) 129 .build(); 130 131 const expectedRects: TraceRect[] = [ 132 new TraceRectBuilder() 133 .setX(0) 134 .setY(0) 135 .setWidth(1) 136 .setHeight(1) 137 .setId('1 layer1') 138 .setName('layer1') 139 .setCornerRadius(0) 140 .setTransform(Transform.EMPTY.matrix) 141 .setDepth(0) 142 .setGroupId(0) 143 .setIsVisible(true) 144 .setOpacity(0) 145 .setIsDisplay(false) 146 .setIsSpy(false) 147 .build(), 148 149 new TraceRectBuilder() 150 .setX(0) 151 .setY(0) 152 .setWidth(2) 153 .setHeight(2) 154 .setId('2 layer2') 155 .setName('layer2') 156 .setCornerRadius(2) 157 .setTransform(Transform.EMPTY.matrix) 158 .setDepth(1) 159 .setGroupId(0) 160 .setIsVisible(false) 161 .setIsDisplay(false) 162 .setIsSpy(false) 163 .build(), 164 165 new TraceRectBuilder() 166 .setX(0) 167 .setY(0) 168 .setWidth(5) 169 .setHeight(5) 170 .setId('4 layerRelativeZ') 171 .setName('layerRelativeZ') 172 .setCornerRadius(0) 173 .setTransform(Transform.EMPTY.matrix) 174 .setDepth(2) 175 .setGroupId(0) 176 .setIsVisible(true) 177 .setOpacity(1) 178 .setIsDisplay(false) 179 .setIsSpy(false) 180 .build(), 181 ]; 182 new ZOrderPathsComputation().setRoot(hierarchyRoot).executeInPlace(); 183 184 computation.setRoot(hierarchyRoot).executeInPlace(); 185 checkLayerRects(hierarchyRoot, expectedRects); 186 }); 187 188 it('handles layer rects with different group ids', () => { 189 const hierarchyRoot = new HierarchyTreeBuilder() 190 .setId('LayerTraceEntry') 191 .setName('root') 192 .setChildren([ 193 { 194 id: 1, 195 name: 'layer1', 196 properties: { 197 id: 1, 198 name: 'layer1', 199 cornerRadius: 0, 200 layerStack: 0, 201 bounds: {left: 0, top: 0, right: 1, bottom: 1}, 202 screenBounds: {left: 0, top: 0, right: 1, bottom: 1}, 203 z: 0, 204 isComputedVisible: true, 205 color: {r: 0, g: 0, b: 0, a: 1}, 206 transform: Transform.EMPTY, 207 } as android.surfaceflinger.ILayerProto, 208 }, 209 { 210 id: 2, 211 name: 'layer2', 212 properties: { 213 id: 2, 214 name: 'layer2', 215 cornerRadius: 0, 216 layerStack: 1, 217 bounds: {left: 0, top: 0, right: 1, bottom: 1}, 218 screenBounds: {left: 0, top: 0, right: 1, bottom: 1}, 219 z: 0, 220 isComputedVisible: true, 221 color: {r: 0, g: 0, b: 0, a: 1}, 222 transform: Transform.EMPTY, 223 } as android.surfaceflinger.ILayerProto, 224 }, 225 ]) 226 .build(); 227 228 const expectedRects: TraceRect[] = [ 229 new TraceRectBuilder() 230 .setX(0) 231 .setY(0) 232 .setWidth(1) 233 .setHeight(1) 234 .setId('1 layer1') 235 .setName('layer1') 236 .setCornerRadius(0) 237 .setTransform(Transform.EMPTY.matrix) 238 .setDepth(0) 239 .setGroupId(0) 240 .setIsVisible(true) 241 .setOpacity(1) 242 .setIsDisplay(false) 243 .setIsSpy(false) 244 .build(), 245 246 new TraceRectBuilder() 247 .setX(0) 248 .setY(0) 249 .setWidth(1) 250 .setHeight(1) 251 .setId('2 layer2') 252 .setName('layer2') 253 .setCornerRadius(0) 254 .setTransform(Transform.EMPTY.matrix) 255 .setDepth(0) 256 .setGroupId(1) 257 .setIsVisible(true) 258 .setOpacity(1) 259 .setIsDisplay(false) 260 .setIsSpy(false) 261 .build(), 262 ]; 263 264 computation.setRoot(hierarchyRoot).executeInPlace(); 265 checkLayerRects(hierarchyRoot, expectedRects); 266 }); 267 268 it('makes display rects', () => { 269 const hierarchyRoot = new HierarchyTreeBuilder() 270 .setId('LayerTraceEntry') 271 .setName('root') 272 .setProperties({ 273 displays: [ 274 { 275 id: 1, 276 layerStack: 0, 277 layerStackSpaceRect: {left: 0, top: 0, right: 5, bottom: 5}, 278 transform: Transform.EMPTY, 279 name: 'Test Display', 280 size: {w: 5, h: 5}, 281 isOn: true, 282 }, 283 { 284 id: 2, 285 layerStack: 0, 286 layerStackSpaceRect: null, 287 size: {w: 5, h: 10}, 288 transform: Transform.EMPTY, 289 name: 'Test Display 2', 290 isOn: true, 291 isVirtual: true, 292 }, 293 { 294 id: 3, 295 layerStack: 0, 296 layerStackSpaceRect: null, 297 size: {w: 5, h: 10}, 298 transform: rotationTransform, 299 name: 'Test Display', 300 isOn: false, 301 isVirtual: true, 302 }, 303 ], 304 }) 305 .build(); 306 307 const expectedDisplayRects = [ 308 new TraceRectBuilder() 309 .setX(0) 310 .setY(0) 311 .setWidth(5) 312 .setHeight(5) 313 .setId('Display - 1') 314 .setName('Test Display') 315 .setCornerRadius(0) 316 .setTransform(Transform.EMPTY.matrix) 317 .setDepth(0) 318 .setGroupId(0) 319 .setIsVisible(false) 320 .setIsDisplay(true) 321 .setIsActiveDisplay(true) 322 .setIsSpy(false) 323 .build(), 324 new TraceRectBuilder() 325 .setX(0) 326 .setY(0) 327 .setWidth(5) 328 .setHeight(10) 329 .setId('Display - 2') 330 .setName('Test Display 2') 331 .setCornerRadius(0) 332 .setTransform(Transform.EMPTY.matrix) 333 .setDepth(1) 334 .setGroupId(0) 335 .setIsVisible(false) 336 .setIsDisplay(true) 337 .setIsActiveDisplay(false) 338 .setIsSpy(false) 339 .build(), 340 new TraceRectBuilder() 341 .setX(0) 342 .setY(0) 343 .setWidth(10) 344 .setHeight(5) 345 .setId('Display - 3') 346 .setName('Test Display (2)') 347 .setCornerRadius(0) 348 .setTransform(Transform.EMPTY.matrix) 349 .setDepth(2) 350 .setGroupId(0) 351 .setIsVisible(false) 352 .setIsDisplay(true) 353 .setIsActiveDisplay(false) 354 .setIsSpy(false) 355 .build(), 356 ]; 357 358 computation.setRoot(hierarchyRoot).executeInPlace(); 359 expect(hierarchyRoot.getRects()).toEqual(expectedDisplayRects); 360 }); 361 362 it('makes display rects with unknown or empty name', () => { 363 const hierarchyRoot = new HierarchyTreeBuilder() 364 .setId('LayerTraceEntry') 365 .setName('root') 366 .setProperties({ 367 displays: [ 368 { 369 id: 1, 370 layerStack: 0, 371 layerStackSpaceRect: {left: 0, top: 0, right: 5, bottom: 5}, 372 transform: Transform.EMPTY, 373 size: {w: 5, h: 5}, 374 }, 375 { 376 id: 1, 377 layerStack: 0, 378 layerStackSpaceRect: {left: 0, top: 0, right: 5, bottom: 5}, 379 transform: Transform.EMPTY, 380 name: '', 381 size: {w: 5, h: 5}, 382 }, 383 ], 384 }) 385 .build(); 386 387 const expectedDisplayRects = [ 388 new TraceRectBuilder() 389 .setX(0) 390 .setY(0) 391 .setWidth(5) 392 .setHeight(5) 393 .setId('Display - 1') 394 .setName('Unknown Display') 395 .setCornerRadius(0) 396 .setTransform(Transform.EMPTY.matrix) 397 .setDepth(0) 398 .setGroupId(0) 399 .setIsVisible(false) 400 .setIsDisplay(true) 401 .setIsSpy(false) 402 .build(), 403 new TraceRectBuilder() 404 .setX(0) 405 .setY(0) 406 .setWidth(5) 407 .setHeight(5) 408 .setId('Display - 1') 409 .setName('Unknown Display (2)') 410 .setCornerRadius(0) 411 .setTransform(Transform.EMPTY.matrix) 412 .setDepth(1) 413 .setGroupId(0) 414 .setIsVisible(false) 415 .setIsDisplay(true) 416 .setIsSpy(false) 417 .build(), 418 ]; 419 420 computation.setRoot(hierarchyRoot).executeInPlace(); 421 expect(hierarchyRoot.getRects()).toEqual(expectedDisplayRects); 422 }); 423 424 it('does not make non-visible rects with missing or invalid screen bounds', () => { 425 const hierarchyRoot = new HierarchyTreeBuilder() 426 .setId('LayerTraceEntry') 427 .setName('root') 428 .setProperties({ 429 displays: [ 430 { 431 id: 1, 432 layerStack: 0, 433 layerStackSpaceRect: {left: 0, top: 0, right: 5, bottom: 10}, 434 transform: Transform.EMPTY, 435 name: 'Test Display', 436 size: {w: 5, h: 10}, 437 }, 438 ], 439 }) 440 .setChildren([ 441 { 442 id: 1, 443 name: 'layer1', 444 properties: { 445 id: 1, 446 name: 'layer1', 447 cornerRadius: 0, 448 layerStack: 0, 449 bounds: {left: -50, top: -100, right: 50, bottom: 100}, 450 screenBounds: {left: -50, top: -100, right: 50, bottom: 100}, 451 z: 0, 452 isComputedVisible: true, 453 transform: Transform.EMPTY, 454 } as android.surfaceflinger.ILayerProto, 455 }, 456 { 457 id: 2, 458 name: 'layer2', 459 properties: { 460 id: 2, 461 name: 'layer2', 462 cornerRadius: 0, 463 layerStack: 0, 464 bounds: {left: -50, top: -100, right: 50, bottom: 100}, 465 screenBounds: {left: -50, top: -100, right: 50, bottom: 100}, 466 z: 0, 467 isComputedVisible: false, 468 transform: Transform.EMPTY, 469 } as android.surfaceflinger.ILayerProto, 470 }, 471 { 472 id: 3, 473 name: 'layer3', 474 properties: { 475 id: 3, 476 name: 'layer3', 477 cornerRadius: 0, 478 layerStack: 0, 479 bounds: {left: -50, top: -100, right: 50, bottom: 100}, 480 screenBounds: {left: -49.991, top: -100, right: 50, bottom: 100}, 481 z: 0, 482 isComputedVisible: false, 483 transform: Transform.EMPTY, 484 } as android.surfaceflinger.ILayerProto, 485 }, 486 { 487 id: 4, 488 name: 'layer4', 489 properties: { 490 id: 4, 491 name: 'layer4', 492 cornerRadius: 0, 493 layerStack: 0, 494 bounds: {left: -50000, top: -50000, right: 50000, bottom: 50000}, 495 screenBounds: { 496 left: -50000, 497 top: -50000, 498 right: 50000, 499 bottom: 50000, 500 }, 501 z: 0, 502 isComputedVisible: false, 503 transform: Transform.EMPTY, 504 } as android.surfaceflinger.ILayerProto, 505 }, 506 { 507 id: 5, 508 name: 'layer5', 509 properties: { 510 id: 5, 511 name: 'layer5', 512 cornerRadius: 0, 513 layerStack: 0, 514 bounds: {left: -100, top: -50, right: 100, bottom: 50}, 515 screenBounds: {left: -100, top: -50, right: 100, bottom: 50}, 516 z: 0, 517 isComputedVisible: false, 518 transform: Transform.EMPTY, 519 } as android.surfaceflinger.ILayerProto, 520 }, 521 { 522 id: 6, 523 name: 'layer6', 524 properties: { 525 id: 6, 526 name: 'layer6', 527 cornerRadius: 0, 528 layerStack: 0, 529 bounds: {left: -50, top: -100, right: 50, bottom: 100}, 530 z: 0, 531 isComputedVisible: true, 532 transform: Transform.EMPTY, 533 } as android.surfaceflinger.ILayerProto, 534 }, 535 ]) 536 .build(); 537 538 const expectedRects = [ 539 new TraceRectBuilder() 540 .setX(-50) 541 .setY(-100) 542 .setWidth(100) 543 .setHeight(200) 544 .setId('1 layer1') 545 .setName('layer1') 546 .setCornerRadius(0) 547 .setTransform(Transform.EMPTY.matrix) 548 .setDepth(1) 549 .setGroupId(0) 550 .setIsVisible(true) 551 .setOpacity(0) 552 .setIsDisplay(false) 553 .setIsSpy(false) 554 .build(), 555 ]; 556 557 computation.setRoot(hierarchyRoot).executeInPlace(); 558 checkLayerRects(hierarchyRoot, expectedRects); 559 }); 560 561 it('calculates invalid screen bounds from all displays present', () => { 562 const hierarchyRoot = new HierarchyTreeBuilder() 563 .setId('LayerTraceEntry') 564 .setName('root') 565 .setProperties({ 566 displays: [ 567 { 568 id: 1, 569 layerStack: 0, 570 layerStackSpaceRect: {left: 0, top: 0, right: 5, bottom: 10}, 571 transform: Transform.EMPTY, 572 name: 'Test Display', 573 size: {w: 5, h: 10}, 574 }, 575 { 576 id: 2, 577 layerStack: 0, 578 layerStackSpaceRect: {left: 0, top: 0, right: 5, bottom: 10}, 579 transform: rotationTransform, 580 name: 'Test Display 2', 581 size: {w: 5, h: 10}, 582 }, 583 ], 584 }) 585 .setChildren([ 586 { 587 id: 1, 588 name: 'layer1', 589 properties: { 590 id: 1, 591 name: 'layer1', 592 cornerRadius: 0, 593 layerStack: 0, 594 bounds: {left: -50, top: -100, right: 50, bottom: 100}, 595 screenBounds: {left: -50, top: -100, right: 50, bottom: 100}, 596 z: 0, 597 isComputedVisible: false, 598 transform: Transform.EMPTY, 599 } as android.surfaceflinger.ILayerProto, 600 }, 601 { 602 id: 2, 603 name: 'layer2', 604 properties: { 605 id: 2, 606 name: 'layer2', 607 cornerRadius: 0, 608 layerStack: 0, 609 bounds: {left: -100, top: -100, right: 100, bottom: 100}, 610 screenBounds: {left: -100, top: -100, right: 100, bottom: 100}, 611 z: 0, 612 isComputedVisible: false, 613 transform: Transform.EMPTY, 614 } as android.surfaceflinger.ILayerProto, 615 }, 616 ]) 617 .build(); 618 619 computation.setRoot(hierarchyRoot).executeInPlace(); 620 checkLayerRects(hierarchyRoot, []); 621 }); 622 623 it('makes input window rects', () => { 624 const hierarchyRoot = new HierarchyTreeBuilder() 625 .setId('LayerTraceEntry') 626 .setName('root') 627 .setProperties({ 628 displays: [ 629 { 630 id: 1, 631 layerStack: 0, 632 layerStackSpaceRect: {left: 0, top: 0, right: 5, bottom: 5}, 633 transform: Transform.EMPTY, 634 name: 'Test Display', 635 size: {w: 5, h: 5}, 636 }, 637 { 638 id: 2, 639 layerStack: 1, 640 layerStackSpaceRect: {left: 0, top: 0, right: 6, bottom: 7}, 641 name: 'Test Display 2', 642 size: {w: 6, h: 7}, 643 }, 644 ], 645 }) 646 .setChildren([ 647 { 648 id: 1, 649 name: 'layer1', 650 properties: { 651 id: 1, 652 name: 'layer1', 653 cornerRadius: 0, 654 layerStack: 0, 655 bounds: {left: 0, top: 0, right: 1, bottom: 1}, 656 screenBounds: {left: 0, top: 0, right: 1, bottom: 1}, 657 z: 0, 658 isComputedVisible: true, 659 transform: Transform.EMPTY, 660 inputWindowInfo: { 661 inputConfig: InputConfig.SPY, 662 frame: {left: 0, top: 0, right: 1, bottom: 1}, 663 visible: true, 664 }, 665 } as android.surfaceflinger.ILayerProto, 666 children: [ 667 { 668 id: 2, 669 name: 'layer2', 670 properties: { 671 id: 2, 672 name: 'layer2', 673 cornerRadius: 2, 674 layerStack: 0, 675 bounds: {left: 0, top: 0, right: 2, bottom: 2}, 676 screenBounds: {left: 0, top: 0, right: 2, bottom: 2}, 677 z: 1, 678 occludedBy: [1], 679 isComputedVisible: false, 680 transform: Transform.EMPTY, 681 inputWindowInfo: { 682 inputConfig: 0, 683 frame: {left: 0, top: 0, right: 2, bottom: 2}, 684 visible: false, 685 }, 686 } as android.surfaceflinger.ILayerProto, 687 }, 688 ], 689 }, 690 { 691 id: 3, 692 name: 'layer3', 693 properties: { 694 id: 3, 695 name: 'layer3', 696 cornerRadius: 2, 697 layerStack: 0, 698 bounds: {left: -999, top: -999, right: 999, bottom: 999}, 699 screenBounds: {left: 0, top: 0, right: 2, bottom: 2}, 700 z: 0, 701 isComputedVisible: false, 702 transform: Transform.EMPTY, 703 inputWindowInfo: { 704 inputConfig: InputConfig.IS_WALLPAPER, 705 frame: {left: -999, top: -999, right: 999, bottom: 999}, 706 visible: true, 707 }, 708 } as android.surfaceflinger.ILayerProto, 709 }, 710 { 711 id: 4, 712 name: 'layerRelativeZ', 713 properties: { 714 id: 4, 715 name: 'layerRelativeZ', 716 cornerRadius: 0, 717 layerStack: 0, 718 bounds: {left: 0, top: 0, right: 5, bottom: 5}, 719 screenBounds: {left: 0, top: 0, right: 5, bottom: 5}, 720 z: 2, 721 isComputedVisible: true, 722 color: {r: 0, g: 0, b: 0, a: 1}, 723 transform: Transform.EMPTY, 724 inputWindowInfo: { 725 inputConfig: 0, 726 frame: {left: 0, top: 0, right: 5, bottom: 5}, 727 }, 728 } as android.surfaceflinger.ILayerProto, 729 }, 730 { 731 id: 5, 732 name: 'noInputLayer', 733 properties: { 734 id: 5, 735 name: 'noInputLayer', 736 cornerRadius: 0, 737 layerStack: 0, 738 bounds: {left: 0, top: 0, right: 5, bottom: 5}, 739 screenBounds: {left: 0, top: 0, right: 5, bottom: 5}, 740 z: 0, 741 isComputedVisible: true, 742 color: {r: 0, g: 0, b: 0, a: 1}, 743 transform: Transform.EMPTY, 744 inputWindowInfo: {}, 745 } as android.surfaceflinger.ILayerProto, 746 }, 747 { 748 id: 6, 749 name: 'notTouchableLayer', 750 properties: { 751 id: 6, 752 name: 'notTouchableLayer', 753 cornerRadius: 0, 754 layerStack: 0, 755 bounds: {left: 0, top: 0, right: 5, bottom: 5}, 756 screenBounds: {left: 0, top: 0, right: 5, bottom: 5}, 757 z: 2, 758 isComputedVisible: true, 759 color: {r: 0, g: 0, b: 0, a: 1}, 760 transform: Transform.EMPTY, 761 inputWindowInfo: { 762 inputConfig: InputConfig.NOT_TOUCHABLE, 763 frame: {left: 0, top: 0, right: 5, bottom: 5}, 764 }, 765 } as android.surfaceflinger.ILayerProto, 766 }, 767 { 768 id: 7, 769 name: 'touchableLayer', 770 properties: { 771 id: 7, 772 name: 'touchableLayer', 773 cornerRadius: 0, 774 layerStack: 0, 775 bounds: {left: 0, top: 0, right: 5, bottom: 5}, 776 screenBounds: {left: 0, top: 0, right: 5, bottom: 5}, 777 z: 2, 778 isComputedVisible: true, 779 color: {r: 0, g: 0, b: 0, a: 1}, 780 transform: Transform.EMPTY, 781 inputWindowInfo: { 782 inputConfig: 0, 783 frame: {left: 0, top: 0, right: 5, bottom: 5}, 784 touchableRegion: { 785 rect: [ 786 { 787 left: 0, 788 top: 0, 789 right: 2, 790 bottom: 2, 791 }, 792 ], 793 }, 794 }, 795 } as android.surfaceflinger.ILayerProto, 796 }, 797 { 798 id: 8, 799 name: 'touchableLayer', 800 properties: { 801 id: 8, 802 name: 'touchableLayer', 803 cornerRadius: 0, 804 layerStack: 1, 805 bounds: {left: 0, top: 0, right: 5, bottom: 5}, 806 screenBounds: {left: 0, top: 0, right: 5, bottom: 5}, 807 z: 2, 808 isComputedVisible: true, 809 color: {r: 0, g: 0, b: 0, a: 1}, 810 transform: Transform.EMPTY, 811 inputWindowInfo: { 812 inputConfig: 0, 813 frame: {left: 0, top: 0, right: 5, bottom: 5}, 814 touchableRegion: { 815 rect: [ 816 { 817 left: 0, 818 top: 0, 819 right: 2, 820 bottom: 2, 821 }, 822 ], 823 }, 824 }, 825 } as android.surfaceflinger.ILayerProto, 826 }, 827 { 828 id: 9, 829 name: 'touchableLayerWithNoFrame', 830 properties: { 831 id: 9, 832 name: 'touchableLayerWithNoFrame', 833 cornerRadius: 0, 834 layerStack: 1, 835 bounds: {left: 0, top: 0, right: 5, bottom: 5}, 836 screenBounds: {left: 0, top: 0, right: 5, bottom: 5}, 837 z: 2, 838 isComputedVisible: true, 839 color: {r: 0, g: 0, b: 0, a: 1}, 840 transform: Transform.EMPTY, 841 inputWindowInfo: { 842 inputConfig: 0, 843 frame: {}, 844 touchableRegion: { 845 rect: [ 846 { 847 left: -999, 848 top: -999, 849 right: 999, 850 bottom: 999, 851 }, 852 ], 853 }, 854 }, 855 } as android.surfaceflinger.ILayerProto, 856 }, 857 ]) 858 .build(); 859 860 const expectedInputRects: TraceRect[] = [ 861 new TraceRectBuilder() 862 .setX(0) 863 .setY(0) 864 .setWidth(1) 865 .setHeight(1) 866 .setId('1 layer1') 867 .setName('layer1') 868 .setCornerRadius(0) 869 .setTransform(Transform.EMPTY.matrix) 870 .setDepth(1) 871 .setGroupId(0) 872 .setIsVisible(true) 873 .setIsDisplay(false) 874 .setIsSpy(true) 875 .build(), 876 877 new TraceRectBuilder() 878 .setX(0) 879 .setY(0) 880 .setWidth(2) 881 .setHeight(2) 882 .setId('2 layer2') 883 .setName('layer2') 884 .setCornerRadius(0) 885 .setTransform(Transform.EMPTY.matrix) 886 .setDepth(2) 887 .setGroupId(0) 888 .setIsVisible(false) 889 .setIsDisplay(false) 890 .setIsSpy(false) 891 .build(), 892 893 // This is a wallpaper window, so it is cropped to display bounds. 894 new TraceRectBuilder() 895 .setX(0) 896 .setY(0) 897 .setWidth(5) 898 .setHeight(5) 899 .setId('3 layer3') 900 .setName('layer3') 901 .setCornerRadius(0) 902 .setTransform(Transform.EMPTY.matrix) 903 .setDepth(3) 904 .setGroupId(0) 905 .setIsVisible(true) 906 .setIsDisplay(false) 907 .setIsSpy(false) 908 .build(), 909 910 new TraceRectBuilder() 911 .setX(0) 912 .setY(0) 913 .setWidth(5) 914 .setHeight(5) 915 .setId('4 layerRelativeZ') 916 .setName('layerRelativeZ') 917 .setCornerRadius(0) 918 .setTransform(Transform.EMPTY.matrix) 919 .setDepth(4) 920 .setGroupId(0) 921 .setIsVisible(true) 922 .setIsDisplay(false) 923 .setIsSpy(false) 924 .build(), 925 926 new TraceRectBuilder() 927 .setX(0) 928 .setY(0) 929 .setWidth(5) 930 .setHeight(5) 931 .setId('6 notTouchableLayer') 932 .setName('notTouchableLayer') 933 .setCornerRadius(0) 934 .setTransform(Transform.EMPTY.matrix) 935 .setDepth(5) 936 .setGroupId(0) 937 .setIsVisible(true) 938 .setIsDisplay(false) 939 .setIsSpy(false) 940 .setFillRegion(Region.createEmpty()) 941 .build(), 942 943 new TraceRectBuilder() 944 .setX(0) 945 .setY(0) 946 .setWidth(5) 947 .setHeight(5) 948 .setId('7 touchableLayer') 949 .setName('touchableLayer') 950 .setCornerRadius(0) 951 .setTransform(Transform.EMPTY.matrix) 952 .setDepth(6) 953 .setGroupId(0) 954 .setIsVisible(true) 955 .setIsDisplay(false) 956 .setIsSpy(false) 957 .setFillRegion(new Region([new Rect(0, 0, 2, 2)])) 958 .build(), 959 960 new TraceRectBuilder() 961 .setX(0) 962 .setY(0) 963 .setWidth(5) 964 .setHeight(5) 965 .setId('8 touchableLayer') 966 .setName('touchableLayer') 967 .setCornerRadius(0) 968 .setTransform(Transform.EMPTY.matrix) 969 .setDepth(1) 970 .setGroupId(1) 971 .setIsVisible(true) 972 .setIsDisplay(false) 973 .setIsSpy(false) 974 .setFillRegion(new Region([new Rect(0, 0, 2, 2)])) 975 .build(), 976 977 new TraceRectBuilder() 978 .setX(0) 979 .setY(0) 980 .setWidth(0) 981 .setHeight(0) 982 .setId('9 touchableLayerWithNoFrame') 983 .setName('touchableLayerWithNoFrame') 984 .setCornerRadius(0) 985 .setTransform(Transform.EMPTY.matrix) 986 .setDepth(2) 987 .setGroupId(1) 988 .setIsVisible(true) 989 .setIsDisplay(false) 990 .setIsSpy(false) 991 .setFillRegion(new Region([new Rect(0, 0, 6, 7)])) 992 .build(), 993 ]; 994 995 const expectedDisplayRects = [ 996 new TraceRectBuilder() 997 .setX(0) 998 .setY(0) 999 .setWidth(5) 1000 .setHeight(5) 1001 .setId('Display - 1') 1002 .setName('Test Display') 1003 .setCornerRadius(0) 1004 .setTransform(Transform.EMPTY.matrix) 1005 .setDepth(0) 1006 .setGroupId(0) 1007 .setIsVisible(false) 1008 .setIsDisplay(true) 1009 .setIsSpy(false) 1010 .build(), 1011 1012 new TraceRectBuilder() 1013 .setX(0) 1014 .setY(0) 1015 .setWidth(6) 1016 .setHeight(7) 1017 .setId('Display - 2') 1018 .setName('Test Display 2') 1019 .setCornerRadius(0) 1020 .setTransform(Transform.EMPTY.matrix) 1021 .setDepth(1) 1022 .setGroupId(1) 1023 .setIsVisible(false) 1024 .setIsDisplay(true) 1025 .setIsSpy(false) 1026 .build(), 1027 ]; 1028 1029 computation.setRoot(hierarchyRoot).executeInPlace(); 1030 checkInputRects(hierarchyRoot, expectedInputRects); 1031 expect(hierarchyRoot.getRects()).toEqual(expectedDisplayRects); 1032 }); 1033 1034 function checkRects( 1035 hierarchyRoot: HierarchyTreeNode, 1036 expectedRects: TraceRect[], 1037 usePrimaryRects: boolean, 1038 ) { 1039 const rects: TraceRect[] = []; 1040 hierarchyRoot.forEachNodeDfs((node) => { 1041 if (node.id === 'LayerTraceEntry root') { 1042 return; 1043 } 1044 const nodeRects = usePrimaryRects 1045 ? node.getRects() 1046 : node.getSecondaryRects(); 1047 if (nodeRects) rects.push(...nodeRects); 1048 }); 1049 expect(rects).toEqual(expectedRects); 1050 } 1051 1052 function checkLayerRects( 1053 hierarchyRoot: HierarchyTreeNode, 1054 expectedRects: TraceRect[], 1055 ) { 1056 checkRects(hierarchyRoot, expectedRects, true); 1057 } 1058 1059 function checkInputRects( 1060 hierarchyRoot: HierarchyTreeNode, 1061 expectedRects: TraceRect[], 1062 ) { 1063 checkRects(hierarchyRoot, expectedRects, false); 1064 } 1065}); 1066