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