xref: /aosp_15_r20/development/tools/winscope/src/app/components/timeline/timeline_utils.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 {TimeRange, Timestamp} from 'common/time';
19import {ComponentTimestampConverter} from 'common/timestamp_converter';
20import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
21
22export class TimelineUtils {
23  static isTransitionWithUnknownStart(transition: PropertyTreeNode): boolean {
24    const shellData = transition.getChildByName('shellData');
25    const dispatchTimestamp: Timestamp | undefined = shellData
26      ?.getChildByName('dispatchTimeNs')
27      ?.getValue();
28    return dispatchTimestamp === undefined;
29  }
30
31  static isTransitionWithUnknownEnd(transition: PropertyTreeNode): boolean {
32    const shellData = transition.getChildByName('shellData');
33    const wmData = transition.getChildByName('wmData');
34    const aborted: boolean = assertDefined(
35      transition.getChildByName('aborted'),
36    ).getValue();
37    const finishOrAbortTimestamp: Timestamp | undefined = aborted
38      ? shellData?.getChildByName('abortTimeNs')?.getValue()
39      : wmData?.getChildByName('finishTimeNs')?.getValue();
40    return finishOrAbortTimestamp === undefined;
41  }
42
43  static getTimeRangeForTransition(
44    transition: PropertyTreeNode,
45    fullTimeRange: TimeRange,
46    converter: ComponentTimestampConverter,
47  ): TimeRange | undefined {
48    const shellData = transition.getChildByName('shellData');
49    const wmData = transition.getChildByName('wmData');
50
51    const aborted: boolean = assertDefined(
52      transition.getChildByName('aborted'),
53    ).getValue();
54
55    const dispatchTimestamp: Timestamp | undefined = shellData
56      ?.getChildByName('dispatchTimeNs')
57      ?.getValue();
58    const createTimestamp: Timestamp | undefined = wmData
59      ?.getChildByName('createTimeNs')
60      ?.getValue();
61    const finishOrAbortTimestamp: Timestamp | undefined = aborted
62      ? shellData?.getChildByName('abortTimeNs')?.getValue()
63      : wmData?.getChildByName('finishTimeNs')?.getValue();
64
65    // currently we only render transitions during 'play' stage, so
66    // do not render if no dispatch time and no finish/shell abort time
67    // or if transition created but never dispatched to shell
68    // TODO (b/324056564): visualise transition lifecycle in timeline
69    if (
70      (!dispatchTimestamp && !finishOrAbortTimestamp) ||
71      (!dispatchTimestamp && createTimestamp)
72    ) {
73      return undefined;
74    }
75
76    const timeRangeMin = fullTimeRange.from.getValueNs();
77    const timeRangeMax = fullTimeRange.to.getValueNs();
78
79    if (
80      finishOrAbortTimestamp &&
81      finishOrAbortTimestamp.getValueNs() < timeRangeMin
82    ) {
83      return undefined;
84    }
85
86    if (
87      !finishOrAbortTimestamp &&
88      assertDefined(dispatchTimestamp).getValueNs() < timeRangeMin
89    ) {
90      return undefined;
91    }
92
93    if (
94      dispatchTimestamp &&
95      finishOrAbortTimestamp &&
96      dispatchTimestamp.getValueNs() > timeRangeMax
97    ) {
98      return undefined;
99    }
100
101    const dispatchTimeNs = dispatchTimestamp
102      ? dispatchTimestamp.getValueNs()
103      : assertDefined(finishOrAbortTimestamp).getValueNs() - 1n;
104
105    const finishTimeNs = finishOrAbortTimestamp
106      ? finishOrAbortTimestamp.getValueNs()
107      : dispatchTimeNs + 1n;
108
109    const startTime = converter.makeTimestampFromNs(
110      dispatchTimeNs > timeRangeMin ? dispatchTimeNs : timeRangeMin,
111    );
112    const finishTime = converter.makeTimestampFromNs(finishTimeNs);
113
114    return new TimeRange(startTime, finishTime);
115  }
116
117  static convertHexToRgb(
118    hex: string,
119  ): {r: number; g: number; b: number} | undefined {
120    // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
121    const shorthandRegex = /^#?([a-f\d])([a-f\d])([a-f\d])$/i;
122    hex = hex.replace(shorthandRegex, (m, r, g, b) => {
123      return r + r + g + g + b + b;
124    });
125
126    const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
127    return result
128      ? {
129          // tslint:disable-next-line:ban
130          r: parseInt(result[1], 16),
131          // tslint:disable-next-line:ban
132          g: parseInt(result[2], 16),
133          // tslint:disable-next-line:ban
134          b: parseInt(result[3], 16),
135        }
136      : undefined;
137  }
138}
139