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