• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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