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