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