xref: /aosp_15_r20/development/tools/winscope/src/parsers/transitions/entry_properties_tree_factory.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
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 {assertDefined} from 'common/assert_utils';
18import {Timestamp} from 'common/time';
19import {ParserTimestampConverter} from 'common/timestamp_converter';
20import {AddDefaults} from 'parsers/operations/add_defaults';
21import {SetFormatters} from 'parsers/operations/set_formatters';
22import {
23  MakeTimestampStrategyType,
24  TransformToTimestamp,
25} from 'parsers/operations/transform_to_timestamp';
26import {TranslateIntDef} from 'parsers/operations/translate_intdef';
27import {TamperedMessageType} from 'parsers/tampered_message_type';
28import {perfetto} from 'protos/transitions/latest/static';
29import root from 'protos/transitions/udc/json';
30import {com} from 'protos/transitions/udc/static';
31import {
32  EnumFormatter,
33  PropertyFormatter,
34  TIMESTAMP_NODE_FORMATTER,
35} from 'trace/tree_node/formatters';
36import {PropertyTreeBuilderFromProto} from 'trace/tree_node/property_tree_builder_from_proto';
37import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
38import {AddDuration} from './operations/add_duration';
39import {AddRealToBootTimeOffsetTimestamp} from './operations/add_real_to_elapsed_time_offset_timestamp';
40import {AddRootProperties} from './operations/add_root_properties';
41import {AddStatus} from './operations/add_status';
42import {UpdateAbortTimeNodes} from './operations/update_abort_time_nodes';
43import {TransitionType} from './transition_type';
44
45interface TransitionInfo {
46  entry:
47    | com.android.server.wm.shell.ITransition
48    | com.android.wm.shell.ITransition
49    | perfetto.protos.IShellTransition;
50  realToBootTimeOffsetNs: bigint | undefined;
51  timestampConverter: ParserTimestampConverter;
52  handlerMapping?: {[key: number]: string};
53}
54
55export class EntryPropertiesTreeFactory {
56  static readonly TRANSITION_OPERATIONS = [
57    new AddDuration(),
58    new AddStatus(),
59    new AddRootProperties(),
60  ];
61
62  private static readonly TransitionTraceProto = TamperedMessageType.tamper(
63    root.lookupType('com.android.server.wm.shell.TransitionTraceProto'),
64  );
65  private static readonly TransitionField =
66    EntryPropertiesTreeFactory.TransitionTraceProto.fields['transitions'];
67  private static readonly WM_ADD_DEFAULTS_OPERATION = new AddDefaults(
68    EntryPropertiesTreeFactory.TransitionField,
69    ['type', 'targets'],
70  );
71  private static WM_INTDEF_OPERATION = new TranslateIntDef(
72    EntryPropertiesTreeFactory.TransitionField,
73  );
74  private static readonly SET_FORMATTERS_OPERATION = new SetFormatters();
75  private static readonly PERFETTO_TRANSITION_OPERATIONS = [
76    new UpdateAbortTimeNodes(),
77  ];
78  private static readonly TRANSITION_TYPE_FORMATTER = new EnumFormatter(
79    TransitionType,
80  );
81
82  static makeTransitionPropertiesTree(
83    shellEntryTree: PropertyTreeNode,
84    wmEntryTree: PropertyTreeNode,
85  ): PropertyTreeNode {
86    const transitionTree = new PropertyTreeNode(
87      wmEntryTree.id,
88      wmEntryTree.name,
89      wmEntryTree.source,
90      undefined,
91    );
92
93    transitionTree.addOrReplaceChild(
94      assertDefined(shellEntryTree.getChildByName('shellData')),
95    );
96    transitionTree.addOrReplaceChild(
97      assertDefined(wmEntryTree.getChildByName('wmData')),
98    );
99    EntryPropertiesTreeFactory.TRANSITION_OPERATIONS.forEach((operation) =>
100      operation.apply(transitionTree),
101    );
102    return transitionTree;
103  }
104
105  static makeWmPropertiesTree(
106    info?: TransitionInfo,
107    denylistProperties: string[] = [],
108  ): PropertyTreeNode {
109    const tree = new PropertyTreeBuilderFromProto()
110      .setData({wmData: info?.entry ?? null})
111      .setRootId('TransitionTraceEntry')
112      .setRootName('Selected Transition')
113      .setDenyList(denylistProperties)
114      .setVisitPrototype(false)
115      .build();
116
117    if (!info) {
118      EntryPropertiesTreeFactory.SET_FORMATTERS_OPERATION.apply(tree);
119      return tree;
120    }
121
122    if (denylistProperties.length > 0) {
123      EntryPropertiesTreeFactory.PERFETTO_TRANSITION_OPERATIONS.forEach(
124        (operation) => operation.apply(tree),
125      );
126    }
127
128    let realToBootTimeOffsetTimestamp: Timestamp | undefined;
129
130    if (info.realToBootTimeOffsetNs !== undefined) {
131      realToBootTimeOffsetTimestamp =
132        info.timestampConverter.makeTimestampFromRealNs(
133          info.realToBootTimeOffsetNs,
134        );
135    }
136
137    const wmDataNode = assertDefined(tree.getChildByName('wmData'));
138    new AddRealToBootTimeOffsetTimestamp(realToBootTimeOffsetTimestamp).apply(
139      wmDataNode,
140    );
141    EntryPropertiesTreeFactory.WM_ADD_DEFAULTS_OPERATION.apply(wmDataNode);
142    new TransformToTimestamp(
143      [
144        'abortTimeNs',
145        'createTimeNs',
146        'sendTimeNs',
147        'finishTimeNs',
148        'startingWindowRemoveTimeNs',
149      ],
150      EntryPropertiesTreeFactory.makeTimestampStrategy(info.timestampConverter),
151    ).apply(wmDataNode);
152
153    const customFormatters = new Map<string, PropertyFormatter>([
154      ['type', EntryPropertiesTreeFactory.TRANSITION_TYPE_FORMATTER],
155      ['mode', EntryPropertiesTreeFactory.TRANSITION_TYPE_FORMATTER],
156      ['abortTimeNs', TIMESTAMP_NODE_FORMATTER],
157      ['createTimeNs', TIMESTAMP_NODE_FORMATTER],
158      ['sendTimeNs', TIMESTAMP_NODE_FORMATTER],
159      ['finishTimeNs', TIMESTAMP_NODE_FORMATTER],
160      ['startingWindowRemoveTimeNs', TIMESTAMP_NODE_FORMATTER],
161    ]);
162
163    new SetFormatters(undefined, customFormatters).apply(tree);
164
165    EntryPropertiesTreeFactory.WM_INTDEF_OPERATION.apply(tree);
166    return tree;
167  }
168
169  static makeShellPropertiesTree(
170    info?: TransitionInfo,
171    denylistProperties: string[] = [],
172  ): PropertyTreeNode {
173    const tree = new PropertyTreeBuilderFromProto()
174      .setData({shellData: info?.entry ?? null})
175      .setRootId('TransitionTraceEntry')
176      .setRootName('Selected Transition')
177      .setDenyList(denylistProperties)
178      .setVisitPrototype(false)
179      .build();
180
181    if (!info) {
182      EntryPropertiesTreeFactory.SET_FORMATTERS_OPERATION.apply(tree);
183      return tree;
184    }
185
186    if (denylistProperties.length > 0) {
187      EntryPropertiesTreeFactory.PERFETTO_TRANSITION_OPERATIONS.forEach(
188        (operation) => operation.apply(tree),
189      );
190    }
191
192    let realToBootTimeOffsetTimestamp: Timestamp | undefined;
193    if (info.realToBootTimeOffsetNs !== undefined) {
194      realToBootTimeOffsetTimestamp =
195        info.timestampConverter.makeTimestampFromRealNs(
196          info.realToBootTimeOffsetNs,
197        );
198    }
199
200    const shellDataNode = assertDefined(tree.getChildByName('shellData'));
201    new AddRealToBootTimeOffsetTimestamp(realToBootTimeOffsetTimestamp).apply(
202      shellDataNode,
203    );
204    new TransformToTimestamp(
205      ['dispatchTimeNs', 'mergeRequestTimeNs', 'mergeTimeNs', 'abortTimeNs'],
206      EntryPropertiesTreeFactory.makeTimestampStrategy(info.timestampConverter),
207    ).apply(shellDataNode);
208
209    const customFormatters = new Map<string, PropertyFormatter>([
210      ['type', EntryPropertiesTreeFactory.TRANSITION_TYPE_FORMATTER],
211      ['mode', EntryPropertiesTreeFactory.TRANSITION_TYPE_FORMATTER],
212      ['dispatchTimeNs', TIMESTAMP_NODE_FORMATTER],
213      ['mergeRequestTimeNs', TIMESTAMP_NODE_FORMATTER],
214      ['mergeTimeNs', TIMESTAMP_NODE_FORMATTER],
215      ['abortTimeNs', TIMESTAMP_NODE_FORMATTER],
216    ]);
217
218    if (info.handlerMapping) {
219      customFormatters.set('handler', new EnumFormatter(info.handlerMapping));
220    }
221
222    new SetFormatters(undefined, customFormatters).apply(tree);
223
224    return tree;
225  }
226
227  private static makeTimestampStrategy(
228    timestampConverter: ParserTimestampConverter,
229  ): MakeTimestampStrategyType {
230    return (valueNs: bigint) => {
231      return timestampConverter.makeTimestampFromBootTimeNs(valueNs);
232    };
233  }
234}
235