1/* 2 * Copyright (C) 2022 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 {PersistentStoreProxy} from 'common/persistent_store_proxy'; 19import {Timestamp} from 'common/time'; 20import {WinscopeEvent, WinscopeEventType} from 'messaging/winscope_event'; 21import {Trace, TraceEntry} from 'trace/trace'; 22import {Traces} from 'trace/traces'; 23import {ImeTraceType, TraceType} from 'trace/trace_type'; 24import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node'; 25import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 26import {TreeNode} from 'trace/tree_node/tree_node'; 27import {ImeAdditionalProperties} from 'viewers/common/ime_additional_properties'; 28import {ImeUiData} from 'viewers/common/ime_ui_data'; 29import { 30 ImeLayers, 31 ImeUtils, 32 ProcessedWindowManagerState, 33} from 'viewers/common/ime_utils'; 34import {TableProperties} from 'viewers/common/table_properties'; 35import {UserOptions} from 'viewers/common/user_options'; 36import { 37 AbstractHierarchyViewerPresenter, 38 NotifyHierarchyViewCallbackType, 39} from './abstract_hierarchy_viewer_presenter'; 40import {VISIBLE_CHIP} from './chip'; 41import {HierarchyPresenter} from './hierarchy_presenter'; 42import {PropertiesPresenter} from './properties_presenter'; 43import {UiHierarchyTreeNode} from './ui_hierarchy_tree_node'; 44import {UiTreeUtils} from './ui_tree_utils'; 45 46export abstract class AbstractPresenterInputMethod extends AbstractHierarchyViewerPresenter { 47 protected getHierarchyTreeNameStrategy = ( 48 entry: TraceEntry<HierarchyTreeNode>, 49 tree: HierarchyTreeNode, 50 ) => { 51 const where = tree.getEagerPropertyByName('where')?.formattedValue(); 52 return entry.getTimestamp().format() + ' - ' + where; 53 }; 54 protected override hierarchyPresenter = new HierarchyPresenter( 55 PersistentStoreProxy.new<UserOptions>( 56 'ImeHierarchyOptions', 57 { 58 simplifyNames: { 59 name: 'Simplify names', 60 enabled: true, 61 }, 62 showOnlyVisible: { 63 name: 'Show only', 64 chip: VISIBLE_CHIP, 65 enabled: false, 66 }, 67 flat: { 68 name: 'Flat', 69 enabled: false, 70 }, 71 }, 72 this.storage, 73 ), 74 [], 75 true, 76 false, 77 this.getHierarchyTreeNameStrategy, 78 ); 79 protected override propertiesPresenter = new PropertiesPresenter( 80 PersistentStoreProxy.new<UserOptions>( 81 'ImePropertiesOptions', 82 { 83 showDefaults: { 84 name: 'Show defaults', 85 enabled: false, 86 tooltip: ` 87 If checked, shows the value of all properties. 88 Otherwise, hides all properties whose value is 89 the default for its data type. 90 `, 91 }, 92 }, 93 this.storage, 94 ), 95 [], 96 ); 97 protected override multiTraceType = undefined; 98 99 protected readonly imeTrace: Trace<HierarchyTreeNode>; 100 private readonly wmTrace?: Trace<HierarchyTreeNode>; 101 private readonly sfTrace?: Trace<HierarchyTreeNode>; 102 103 private hierarchyTableProperties: TableProperties | undefined; 104 private additionalProperties: ImeAdditionalProperties | undefined; 105 106 constructor( 107 trace: Trace<HierarchyTreeNode>, 108 traces: Traces, 109 storage: Storage, 110 notifyViewCallback: NotifyHierarchyViewCallbackType, 111 ) { 112 super( 113 trace, 114 traces, 115 storage, 116 notifyViewCallback, 117 new ImeUiData(trace.type as ImeTraceType), 118 ); 119 this.imeTrace = trace; 120 this.sfTrace = traces.getTrace(TraceType.SURFACE_FLINGER); 121 this.wmTrace = traces.getTrace(TraceType.WINDOW_MANAGER); 122 } 123 124 async onAppEvent(event: WinscopeEvent) { 125 await event.visit( 126 WinscopeEventType.TRACE_POSITION_UPDATE, 127 async (event) => { 128 this.clearOverridePropertiesTreeSelection(); 129 await this.applyTracePositionUpdate(event); 130 131 const imeEntry = this.hierarchyPresenter.getCurrentEntryForTrace( 132 this.imeTrace, 133 ); 134 const [sfEntry, wmEntry] = this.findSfWmTraceEntries(imeEntry); 135 136 if (imeEntry) { 137 this.additionalProperties = await this.getAdditionalProperties( 138 await wmEntry?.getValue(), 139 await sfEntry?.getValue(), 140 wmEntry?.getTimestamp(), 141 sfEntry?.getTimestamp(), 142 ); 143 this.hierarchyTableProperties = this.getHierarchyTableProperties(); 144 145 await this.updateOverridePropertiesTree(this.additionalProperties); 146 147 const highlightedItem = this.getHighlightedItem(); 148 const selected = this.hierarchyPresenter.getSelectedTree(); 149 150 if (!selected && highlightedItem !== undefined) { 151 const isHighlightedFilter = (node: HierarchyTreeNode) => 152 UiTreeUtils.isHighlighted(node, highlightedItem); 153 let selectedTree = 154 this.additionalProperties?.sf?.taskLayerOfImeContainer?.findDfs( 155 isHighlightedFilter, 156 ); 157 if (!selectedTree) { 158 selectedTree = 159 this.additionalProperties?.sf?.taskLayerOfImeSnapshot?.findDfs( 160 isHighlightedFilter, 161 ); 162 } 163 164 if (selectedTree) { 165 this.hierarchyPresenter.setSelectedTree([ 166 assertDefined(this.sfTrace), 167 selectedTree, 168 ]); 169 await this.updatePropertiesTree(); 170 } 171 } 172 } 173 this.refreshUIData(); 174 }, 175 ); 176 } 177 178 async onHighlightedNodeChange(node: UiHierarchyTreeNode) { 179 this.clearOverridePropertiesTreeSelection(); 180 await this.applyHighlightedNodeChange(node); 181 this.refreshUIData(); 182 } 183 184 async onHighlightedIdChange(newId: string) { 185 const selectedHierarchyTree = this.hierarchyPresenter.getSelectedTree(); 186 if (!selectedHierarchyTree || selectedHierarchyTree[1].id !== newId) { 187 this.clearOverridePropertiesTreeSelection(); 188 } 189 await this.applyHighlightedIdChange(newId); 190 this.refreshUIData(); 191 } 192 193 async onAdditionalPropertySelected(selectedItem: { 194 name: string; 195 treeNode: TreeNode; 196 }) { 197 this.updateHighlightedItem(selectedItem.treeNode.id); 198 if (selectedItem.treeNode instanceof HierarchyTreeNode) { 199 this.clearOverridePropertiesTreeSelection(); 200 this.hierarchyPresenter.setSelectedTree([ 201 assertDefined(this.wmTrace), 202 selectedItem.treeNode, 203 ]); 204 } else if (selectedItem.treeNode instanceof PropertyTreeNode) { 205 this.hierarchyPresenter.setSelectedTree(undefined); 206 this.overridePropertiesTree = selectedItem.treeNode; 207 } 208 209 this.overridePropertiesTreeName = selectedItem.name; 210 await this.updatePropertiesTree(); 211 this.refreshUIData(); 212 } 213 214 protected async getAdditionalProperties( 215 wmEntry: HierarchyTreeNode | undefined, 216 sfEntry: HierarchyTreeNode | undefined, 217 wmEntryTimestamp: Timestamp | undefined, 218 sfEntryTimestamp: Timestamp | undefined, 219 ): Promise<ImeAdditionalProperties> { 220 let wmProperties: ProcessedWindowManagerState | undefined; 221 let sfProperties: ImeLayers | undefined; 222 223 if (wmEntry) { 224 wmProperties = ImeUtils.processWindowManagerTraceEntry( 225 wmEntry, 226 wmEntryTimestamp, 227 ); 228 229 if (sfEntry) { 230 sfProperties = ImeUtils.getImeLayers( 231 sfEntry, 232 wmProperties, 233 sfEntryTimestamp, 234 ); 235 236 if (sfProperties) { 237 await this.makeSfSubtrees(sfProperties); 238 } 239 } 240 } 241 242 return new ImeAdditionalProperties(wmProperties, sfProperties); 243 } 244 245 protected override keepCalculated(tree: HierarchyTreeNode): boolean { 246 return false; 247 } 248 249 protected override getOverrideDisplayName( 250 selected: [Trace<HierarchyTreeNode>, HierarchyTreeNode], 251 ): string | undefined { 252 return this.overridePropertiesTreeName; 253 } 254 255 private async makeSfSubtrees( 256 sfProperties: ImeLayers, 257 ): Promise<UiHierarchyTreeNode[]> { 258 const sfHierarchyTrees = []; 259 const sfTrace = assertDefined(this.sfTrace); 260 if (sfProperties.taskLayerOfImeContainer) { 261 sfHierarchyTrees.push(sfProperties.taskLayerOfImeContainer); 262 } 263 if (sfProperties.taskLayerOfImeSnapshot) { 264 sfHierarchyTrees.push(sfProperties.taskLayerOfImeSnapshot); 265 } 266 if (sfHierarchyTrees.length > 0) { 267 await this.hierarchyPresenter.addCurrentHierarchyTrees( 268 [sfTrace, sfHierarchyTrees], 269 this.getHighlightedItem(), 270 ); 271 } 272 const sfSubtrees = assertDefined( 273 this.hierarchyPresenter.getFormattedTreesByTrace(sfTrace), 274 ); 275 sfSubtrees.forEach((subtree) => 276 subtree.setDisplayName('SfSubtree - ' + subtree.name), 277 ); 278 return sfSubtrees; 279 } 280 281 private clearOverridePropertiesTreeSelection() { 282 this.overridePropertiesTree = undefined; 283 this.overridePropertiesTreeName = undefined; 284 } 285 286 private findSfWmTraceEntries( 287 imeEntry: TraceEntry<HierarchyTreeNode> | undefined, 288 ): [ 289 TraceEntry<HierarchyTreeNode> | undefined, 290 TraceEntry<HierarchyTreeNode> | undefined, 291 ] { 292 if (!imeEntry || !this.imeTrace.hasFrameInfo()) { 293 return [undefined, undefined]; 294 } 295 296 const frames = imeEntry.getFramesRange(); 297 if (!frames || frames.start === frames.end) { 298 return [undefined, undefined]; 299 } 300 301 const frame = frames.start; 302 const sfEntry = this.sfTrace 303 ?.getFrame(frame) 304 ?.findClosestEntry(imeEntry.getTimestamp()); 305 const wmEntry = this.wmTrace 306 ?.getFrame(frame) 307 ?.findClosestEntry(imeEntry.getTimestamp()); 308 309 return [sfEntry, wmEntry]; 310 } 311 312 private async updateOverridePropertiesTree( 313 additionalProperties: ImeAdditionalProperties, 314 ) { 315 const highlightedItem = this.getHighlightedItem(); 316 if (!highlightedItem) { 317 this.clearOverridePropertiesTreeSelection(); 318 return; 319 } 320 if (highlightedItem.includes('WindowManagerState')) { 321 this.overridePropertiesTree = undefined; 322 const wmHierarchyTree = additionalProperties.wm?.hierarchyTree; 323 this.hierarchyPresenter.setSelectedTree( 324 wmHierarchyTree 325 ? [assertDefined(this.wmTrace), wmHierarchyTree] 326 : undefined, 327 ); 328 this.overridePropertiesTreeName = wmHierarchyTree 329 ? 'Window Manager State' 330 : undefined; 331 } else if (highlightedItem.includes('imeInsetsSourceProvider')) { 332 this.hierarchyPresenter.setSelectedTree(undefined); 333 const imeInsetsSourceProvider = 334 additionalProperties.wm?.wmStateProperties?.imeInsetsSourceProvider; 335 this.overridePropertiesTree = imeInsetsSourceProvider; 336 this.overridePropertiesTreeName = imeInsetsSourceProvider 337 ? 'Ime Insets Source Provider' 338 : undefined; 339 } else if (highlightedItem.includes('inputMethodControlTarget')) { 340 this.hierarchyPresenter.setSelectedTree(undefined); 341 const imeControlTarget = 342 additionalProperties.wm?.wmStateProperties?.imeControlTarget; 343 this.overridePropertiesTree = imeControlTarget; 344 this.overridePropertiesTreeName = imeControlTarget 345 ? 'Ime Control Target' 346 : undefined; 347 } else if (highlightedItem.includes('inputMethodInputTarget')) { 348 this.hierarchyPresenter.setSelectedTree(undefined); 349 const imeInputTarget = 350 additionalProperties.wm?.wmStateProperties?.imeInputTarget; 351 this.overridePropertiesTree = imeInputTarget; 352 this.overridePropertiesTreeName = imeInputTarget 353 ? 'Ime Input Target' 354 : undefined; 355 } else if (highlightedItem.includes('inputMethodTarget')) { 356 this.hierarchyPresenter.setSelectedTree(undefined); 357 const imeLayeringTarget = 358 additionalProperties.wm?.wmStateProperties?.imeLayeringTarget; 359 this.overridePropertiesTree = imeLayeringTarget; 360 this.overridePropertiesTreeName = imeLayeringTarget 361 ? 'Ime Layering Target' 362 : undefined; 363 } 364 365 await this.updatePropertiesTree(); 366 } 367 368 private refreshUIData() { 369 this.refreshHierarchyViewerUiData( 370 new ImeUiData( 371 this.imeTrace.type as ImeTraceType, 372 this.hierarchyTableProperties, 373 this.additionalProperties, 374 ), 375 ); 376 } 377 378 protected abstract getHierarchyTableProperties(): TableProperties; 379} 380