• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 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 {assertDefined} from 'common/assert_utils';
18import {PersistentStoreProxy} from 'common/store/persistent_store_proxy';
19import {Store} from 'common/store/store';
20import {Timestamp} from 'common/time/time';
21import {Trace, TraceEntry} from 'trace/trace';
22import {Traces} from 'trace/traces';
23import {ImeTraceType, TraceType} from 'trace/trace_type';
24import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
25import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
26import {TreeNode} from 'trace/tree_node/tree_node';
27import {ImeAdditionalProperties} from 'viewers/common/ime_additional_properties';
28import {ImeUiData} from 'viewers/common/ime_ui_data';
29import {
30  ImeLayers,
31  ImeUtils,
32  ProcessedWindowManagerState,
33} from 'viewers/common/ime_utils';
34import {TableProperties} from 'viewers/common/table_properties';
35import {TextFilter} from 'viewers/common/text_filter';
36import {UserOptions} from 'viewers/common/user_options';
37import {
38  AbstractHierarchyViewerPresenter,
39  NotifyHierarchyViewCallbackType,
40} from './abstract_hierarchy_viewer_presenter';
41import {VISIBLE_CHIP} from './chip';
42import {HierarchyPresenter} from './hierarchy_presenter';
43import {UpdateSfSubtreeDisplayNames} from './operations/update_sf_subtree_display_names';
44import {PropertiesPresenter} from './properties_presenter';
45import {UiHierarchyTreeNode} from './ui_hierarchy_tree_node';
46import {UiTreeUtils} from './ui_tree_utils';
47import {ViewerEvents} from './viewer_events';
48
49export abstract class AbstractPresenterInputMethod extends AbstractHierarchyViewerPresenter<ImeUiData> {
50  protected getHierarchyTreeNameStrategy = (
51    entry: TraceEntry<HierarchyTreeNode>,
52    tree: HierarchyTreeNode,
53  ) => {
54    const where = tree.getEagerPropertyByName('where')?.formattedValue();
55    return this.getEntryFormattedTimestamp(entry) + ' - ' + where;
56  };
57  protected override hierarchyPresenter = new HierarchyPresenter(
58    PersistentStoreProxy.new<UserOptions>(
59      'ImeHierarchyOptions',
60      {
61        simplifyNames: {
62          name: 'Simplify names',
63          enabled: true,
64        },
65        showOnlyVisible: {
66          name: 'Show only',
67          chip: VISIBLE_CHIP,
68          enabled: false,
69        },
70        flat: {
71          name: 'Flat',
72          enabled: false,
73        },
74      },
75      this.storage,
76    ),
77    new TextFilter(),
78    [],
79    true,
80    false,
81    this.getHierarchyTreeNameStrategy,
82    [[TraceType.SURFACE_FLINGER, [new UpdateSfSubtreeDisplayNames()]]],
83  );
84  protected override propertiesPresenter = new PropertiesPresenter(
85    PersistentStoreProxy.new<UserOptions>(
86      'ImePropertiesOptions',
87      {
88        showDefaults: {
89          name: 'Show defaults',
90          enabled: false,
91          tooltip: `If checked, shows the value of all properties.
92Otherwise, hides all properties whose value is
93the default for its data type.`,
94        },
95      },
96      this.storage,
97    ),
98    new TextFilter(),
99    [],
100  );
101  protected override multiTraceType = undefined;
102
103  protected readonly imeTrace: Trace<HierarchyTreeNode>;
104  private readonly wmTrace?: Trace<HierarchyTreeNode>;
105  private readonly sfTrace?: Trace<HierarchyTreeNode>;
106
107  private hierarchyTableProperties: TableProperties | undefined;
108  private additionalProperties: ImeAdditionalProperties | undefined;
109
110  constructor(
111    trace: Trace<HierarchyTreeNode>,
112    traces: Traces,
113    storage: Store,
114    notifyViewCallback: NotifyHierarchyViewCallbackType<ImeUiData>,
115  ) {
116    super(
117      trace,
118      traces,
119      storage,
120      notifyViewCallback,
121      new ImeUiData(trace.type as ImeTraceType),
122    );
123    this.imeTrace = trace;
124    this.sfTrace = traces.getTrace(TraceType.SURFACE_FLINGER);
125    this.wmTrace = traces.getTrace(TraceType.WINDOW_MANAGER);
126  }
127
128  async onHighlightedNodeChange(node: UiHierarchyTreeNode) {
129    this.clearOverridePropertiesTreeSelection();
130    await this.applyHighlightedNodeChange(node);
131    this.refreshUIData();
132  }
133
134  async onHighlightedIdChange(newId: string) {
135    const selectedHierarchyTree = this.hierarchyPresenter.getSelectedTree();
136    if (!selectedHierarchyTree || selectedHierarchyTree.tree.id !== newId) {
137      this.clearOverridePropertiesTreeSelection();
138    }
139    await this.applyHighlightedIdChange(newId);
140    this.refreshUIData();
141  }
142
143  async onAdditionalPropertySelected(selectedItem: {
144    name: string;
145    treeNode: TreeNode;
146  }) {
147    this.updateHighlightedItem(selectedItem.treeNode.id);
148    if (selectedItem.treeNode instanceof HierarchyTreeNode) {
149      this.clearOverridePropertiesTreeSelection();
150      this.hierarchyPresenter.setSelectedTree({
151        trace: assertDefined(this.wmTrace),
152        tree: selectedItem.treeNode,
153        index: 0,
154      });
155    } else if (selectedItem.treeNode instanceof PropertyTreeNode) {
156      this.hierarchyPresenter.setSelectedTree(undefined);
157      this.overridePropertiesTree = selectedItem.treeNode;
158    }
159
160    this.overridePropertiesTreeName = selectedItem.name;
161    await this.updatePropertiesTree();
162    this.refreshUIData();
163  }
164
165  protected async getAdditionalProperties(
166    wmEntry: HierarchyTreeNode | undefined,
167    sfEntry: HierarchyTreeNode | undefined,
168    wmEntryTimestamp: Timestamp | undefined,
169    sfEntryTimestamp: Timestamp | undefined,
170  ): Promise<ImeAdditionalProperties> {
171    let wmProperties: ProcessedWindowManagerState | undefined;
172    let sfProperties: ImeLayers | undefined;
173
174    if (wmEntry) {
175      wmProperties = ImeUtils.processWindowManagerTraceEntry(
176        wmEntry,
177        wmEntryTimestamp,
178      );
179
180      if (sfEntry) {
181        sfProperties = ImeUtils.getImeLayers(
182          sfEntry,
183          wmProperties,
184          sfEntryTimestamp,
185        );
186
187        if (sfProperties) {
188          await this.makeSfSubtrees(sfProperties);
189        }
190      }
191    }
192
193    return new ImeAdditionalProperties(wmProperties, sfProperties);
194  }
195
196  protected override keepCalculated(tree: HierarchyTreeNode): boolean {
197    return false;
198  }
199
200  protected override getOverrideDisplayName(): string | undefined {
201    return this.overridePropertiesTreeName;
202  }
203
204  protected override async initializeIfNeeded(): Promise<void> {
205    this.clearOverridePropertiesTreeSelection();
206  }
207
208  protected override async processDataAfterPositionUpdate(): Promise<void> {
209    const imeEntry = this.hierarchyPresenter.getCurrentEntryForTrace(
210      this.imeTrace,
211    );
212    const [sfEntry, wmEntry] = this.findSfWmTraceEntries(imeEntry);
213
214    if (imeEntry) {
215      this.additionalProperties = await this.getAdditionalProperties(
216        await wmEntry?.getValue(),
217        await sfEntry?.getValue(),
218        wmEntry?.getTimestamp(),
219        sfEntry?.getTimestamp(),
220      );
221      this.hierarchyTableProperties = this.getHierarchyTableProperties();
222
223      await this.updateOverridePropertiesTree(this.additionalProperties);
224
225      const highlightedItem = this.getHighlightedItem();
226      const selected = this.hierarchyPresenter.getSelectedTree();
227
228      if (!selected && highlightedItem !== undefined) {
229        const isHighlightedFilter = (node: HierarchyTreeNode) =>
230          UiTreeUtils.isHighlighted(node, highlightedItem);
231        let selectedTree =
232          this.additionalProperties?.sf?.taskLayerOfImeContainer?.findDfs(
233            isHighlightedFilter,
234          );
235        if (!selectedTree) {
236          selectedTree =
237            this.additionalProperties?.sf?.taskLayerOfImeSnapshot?.findDfs(
238              isHighlightedFilter,
239            );
240        }
241
242        if (selectedTree) {
243          this.hierarchyPresenter.setSelectedTree({
244            trace: assertDefined(this.sfTrace),
245            tree: selectedTree,
246            index: 1,
247          });
248          await this.updatePropertiesTree();
249        }
250      }
251    }
252  }
253
254  protected override addViewerSpecificListeners(htmlElement: HTMLElement) {
255    htmlElement.addEventListener(
256      ViewerEvents.AdditionalPropertySelected,
257      async (event) =>
258        await this.onAdditionalPropertySelected(
259          (event as CustomEvent).detail.selectedItem,
260        ),
261    );
262  }
263
264  private async makeSfSubtrees(
265    sfProperties: ImeLayers,
266  ): Promise<UiHierarchyTreeNode[]> {
267    const sfHierarchyTrees = [];
268    const sfTrace = assertDefined(this.sfTrace);
269    if (sfProperties.taskLayerOfImeContainer) {
270      sfHierarchyTrees.push(sfProperties.taskLayerOfImeContainer);
271    }
272    if (sfProperties.taskLayerOfImeSnapshot) {
273      sfHierarchyTrees.push(sfProperties.taskLayerOfImeSnapshot);
274    }
275    if (sfHierarchyTrees.length > 0) {
276      await this.hierarchyPresenter.addCurrentHierarchyTrees(
277        {trace: sfTrace, trees: sfHierarchyTrees},
278        this.getHighlightedItem(),
279      );
280    }
281    const sfSubtrees = assertDefined(
282      this.hierarchyPresenter.getFormattedTreesByTrace(sfTrace),
283    );
284    sfSubtrees.forEach((subtree) =>
285      subtree.setDisplayName('SfSubtree - ' + subtree.name),
286    );
287    return sfSubtrees;
288  }
289
290  private clearOverridePropertiesTreeSelection() {
291    this.overridePropertiesTree = undefined;
292    this.overridePropertiesTreeName = undefined;
293  }
294
295  private findSfWmTraceEntries(
296    imeEntry: TraceEntry<HierarchyTreeNode> | undefined,
297  ): [
298    TraceEntry<HierarchyTreeNode> | undefined,
299    TraceEntry<HierarchyTreeNode> | undefined,
300  ] {
301    if (!imeEntry || !this.imeTrace.hasFrameInfo()) {
302      return [undefined, undefined];
303    }
304
305    const frames = imeEntry.getFramesRange();
306    if (!frames || frames.start === frames.end) {
307      return [undefined, undefined];
308    }
309
310    const frame = frames.start;
311    const sfEntry = this.sfTrace
312      ?.getFrame(frame)
313      ?.findClosestEntry(imeEntry.getTimestamp());
314    const wmEntry = this.wmTrace
315      ?.getFrame(frame)
316      ?.findClosestEntry(imeEntry.getTimestamp());
317
318    return [sfEntry, wmEntry];
319  }
320
321  private async updateOverridePropertiesTree(
322    additionalProperties: ImeAdditionalProperties,
323  ) {
324    const highlightedItem = this.getHighlightedItem();
325    if (!highlightedItem) {
326      this.clearOverridePropertiesTreeSelection();
327      return;
328    }
329    if (highlightedItem.includes('WindowManagerState')) {
330      this.overridePropertiesTree = undefined;
331      const wmHierarchyTree = additionalProperties.wm?.hierarchyTree;
332      this.hierarchyPresenter.setSelectedTree(
333        wmHierarchyTree
334          ? {
335              trace: assertDefined(this.wmTrace),
336              tree: wmHierarchyTree,
337              index: 0,
338            }
339          : undefined,
340      );
341      this.overridePropertiesTreeName = wmHierarchyTree
342        ? 'Window Manager State'
343        : undefined;
344    } else if (highlightedItem.includes('imeInsetsSourceProvider')) {
345      this.hierarchyPresenter.setSelectedTree(undefined);
346      const imeInsetsSourceProvider =
347        additionalProperties.wm?.wmStateProperties?.imeInsetsSourceProvider;
348      this.overridePropertiesTree = imeInsetsSourceProvider;
349      this.overridePropertiesTreeName = imeInsetsSourceProvider
350        ? 'Ime Insets Source Provider'
351        : undefined;
352    } else if (highlightedItem.includes('inputMethodControlTarget')) {
353      this.hierarchyPresenter.setSelectedTree(undefined);
354      const imeControlTarget =
355        additionalProperties.wm?.wmStateProperties?.imeControlTarget;
356      this.overridePropertiesTree = imeControlTarget;
357      this.overridePropertiesTreeName = imeControlTarget
358        ? 'Ime Control Target'
359        : undefined;
360    } else if (highlightedItem.includes('inputMethodInputTarget')) {
361      this.hierarchyPresenter.setSelectedTree(undefined);
362      const imeInputTarget =
363        additionalProperties.wm?.wmStateProperties?.imeInputTarget;
364      this.overridePropertiesTree = imeInputTarget;
365      this.overridePropertiesTreeName = imeInputTarget
366        ? 'Ime Input Target'
367        : undefined;
368    } else if (highlightedItem.includes('inputMethodTarget')) {
369      this.hierarchyPresenter.setSelectedTree(undefined);
370      const imeLayeringTarget =
371        additionalProperties.wm?.wmStateProperties?.imeLayeringTarget;
372      this.overridePropertiesTree = imeLayeringTarget;
373      this.overridePropertiesTreeName = imeLayeringTarget
374        ? 'Ime Layering Target'
375        : undefined;
376    }
377
378    await this.updatePropertiesTree();
379  }
380
381  protected override refreshUIData() {
382    this.uiData.hierarchyTableProperties = this.hierarchyTableProperties;
383    this.uiData.additionalProperties = this.additionalProperties;
384    this.refreshHierarchyViewerUiData();
385  }
386
387  protected abstract getHierarchyTableProperties(): TableProperties;
388}
389