1/*
2 * Copyright (C) 2023 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 {INVALID_TIME_NS, Timestamp} from 'common/time';
18import {AbstractParser} from 'parsers/legacy/abstract_parser';
19import {ParserTransitionsUtils} from 'parsers/transitions/parser_transitions_utils';
20import root from 'protos/transitions/udc/json';
21import {com} from 'protos/transitions/udc/static';
22import {TraceType} from 'trace/trace_type';
23import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
24
25export class ParserTransitionsShell extends AbstractParser<PropertyTreeNode> {
26  private static readonly WmShellTransitionsTraceProto = root.lookupType(
27    'com.android.wm.shell.WmShellTransitionTraceProto',
28  );
29
30  private realToBootTimeOffsetNs: bigint | undefined;
31  private handlerMapping: undefined | {[key: number]: string};
32
33  override getTraceType(): TraceType {
34    return TraceType.SHELL_TRANSITION;
35  }
36
37  override getRealToBootTimeOffsetNs(): bigint | undefined {
38    return this.realToBootTimeOffsetNs;
39  }
40
41  override getRealToMonotonicTimeOffsetNs(): bigint | undefined {
42    return undefined;
43  }
44
45  override decodeTrace(
46    traceBuffer: Uint8Array,
47  ): com.android.wm.shell.ITransition[] {
48    const decodedProto =
49      ParserTransitionsShell.WmShellTransitionsTraceProto.decode(
50        traceBuffer,
51      ) as unknown as com.android.wm.shell.IWmShellTransitionTraceProto;
52
53    const timeOffset = BigInt(
54      decodedProto.realToElapsedTimeOffsetNanos?.toString() ?? '0',
55    );
56    this.realToBootTimeOffsetNs = timeOffset !== 0n ? timeOffset : undefined;
57
58    this.handlerMapping = {};
59    for (const mapping of decodedProto.handlerMappings ?? []) {
60      this.handlerMapping[mapping.id] = mapping.name;
61    }
62
63    return decodedProto.transitions ?? [];
64  }
65
66  override processDecodedEntry(
67    index: number,
68    entryProto: com.android.wm.shell.ITransition,
69  ): PropertyTreeNode {
70    return this.makePropertiesTree(entryProto);
71  }
72
73  protected override getTimestamp(
74    entry: com.android.wm.shell.ITransition,
75  ): Timestamp {
76    // for consistency with all transitions, elapsed nanos are defined as shell dispatch time else 0n
77    return entry.dispatchTimeNs
78      ? this.timestampConverter.makeTimestampFromBootTimeNs(
79          BigInt(entry.dispatchTimeNs.toString()),
80        )
81      : this.timestampConverter.makeTimestampFromBootTimeNs(INVALID_TIME_NS);
82  }
83
84  protected getMagicNumber(): number[] | undefined {
85    return [0x09, 0x57, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WMSTRACE
86  }
87
88  private validateShellTransitionEntry(
89    entry: com.android.wm.shell.ITransition,
90  ) {
91    if (entry.id === 0) {
92      throw new Error('Proto needs a non-null id');
93    }
94    if (
95      !entry.dispatchTimeNs &&
96      !entry.mergeRequestTimeNs &&
97      !entry.mergeTimeNs &&
98      !entry.abortTimeNs
99    ) {
100      throw new Error('Requires at least one non-null timestamp');
101    }
102    if (this.realToBootTimeOffsetNs === undefined) {
103      throw new Error('missing realToBootTimeOffsetNs');
104    }
105    if (this.handlerMapping === undefined) {
106      throw new Error('Missing handler mapping');
107    }
108  }
109
110  private makePropertiesTree(
111    entryProto: com.android.wm.shell.ITransition,
112  ): PropertyTreeNode {
113    this.validateShellTransitionEntry(entryProto);
114
115    const shellEntryTree = ParserTransitionsUtils.makeShellPropertiesTree({
116      entry: entryProto,
117      realToBootTimeOffsetNs: this.realToBootTimeOffsetNs,
118      handlerMapping: this.handlerMapping,
119      timestampConverter: this.timestampConverter,
120    });
121    const wmEntryTree = ParserTransitionsUtils.makeWmPropertiesTree();
122
123    return ParserTransitionsUtils.makeTransitionPropertiesTree(
124      shellEntryTree,
125      wmEntryTree,
126    );
127  }
128}
129