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 {com} from 'protos/windowmanager/latest/static'; 19import { 20 LazyPropertiesStrategyType, 21 PropertiesProvider, 22} from 'trace/tree_node/properties_provider'; 23import {PropertiesProviderBuilder} from 'trace/tree_node/properties_provider_builder'; 24import {PropertyTreeBuilderFromProto} from 'trace/tree_node/property_tree_builder_from_proto'; 25import {PropertyTreeNode} from 'trace/tree_node/property_tree_node'; 26import {DEFAULT_PROPERTY_TREE_NODE_FACTORY} from 'trace/tree_node/property_tree_node_factory'; 27import {WindowTypePrefix} from 'trace/window_type'; 28import {OperationLists, WM_OPERATION_LISTS} from './operations/operation_lists'; 29import {WM_DENYLIST_PROPERTIES} from './wm_denylist_properties'; 30import {WM_EAGER_PROPERTIES} from './wm_eager_properties'; 31import {WmProtoType} from './wm_proto_type'; 32 33type WindowContainerChildType = 34 | com.android.server.wm.IWindowContainerProto 35 | com.android.server.wm.IDisplayContentProto 36 | com.android.server.wm.IDisplayAreaProto 37 | com.android.server.wm.ITaskProto 38 | com.android.server.wm.IActivityRecordProto 39 | com.android.server.wm.IWindowTokenProto 40 | com.android.server.wm.IWindowStateProto 41 | com.android.server.wm.ITaskFragmentProto; 42 43class ParserWindowManagerUtils { 44 makeEntryProperties( 45 entryProto: com.android.server.wm.IWindowManagerServiceDumpProto, 46 ): PropertiesProvider { 47 const operations = assertDefined( 48 WM_OPERATION_LISTS.get(WmProtoType.WindowManagerService), 49 ); 50 return new PropertiesProviderBuilder() 51 .setEagerProperties( 52 this.makeEntryEagerPropertiesTree(assertDefined(entryProto)), 53 ) 54 .setLazyPropertiesStrategy( 55 this.makeEntryLazyPropertiesStrategy(assertDefined(entryProto)), 56 ) 57 .setCommonOperations(operations.common) 58 .setEagerOperations(operations.eager) 59 .setLazyOperations(operations.lazy) 60 .build(); 61 } 62 63 extractContainers( 64 entryProto: com.android.server.wm.IWindowManagerServiceDumpProto, 65 ): PropertiesProvider[] { 66 let currChildren: com.android.server.wm.IWindowContainerChildProto[] = 67 assertDefined(entryProto.rootWindowContainer?.windowContainer?.children); 68 69 const rootContainer = assertDefined(entryProto.rootWindowContainer); 70 const rootContainerProperties = this.getContainerChildProperties( 71 rootContainer, 72 currChildren, 73 WM_OPERATION_LISTS.get(WmProtoType.RootWindowContainer), 74 ); 75 76 const containers = [rootContainerProperties]; 77 78 while (currChildren && currChildren.length > 0) { 79 const nextChildren: com.android.server.wm.IWindowContainerChildProto[] = 80 []; 81 containers.push( 82 ...currChildren.map( 83 ( 84 containerChild: com.android.server.wm.IWindowContainerChildProto, 85 ) => { 86 const children = this.getChildren(containerChild); 87 nextChildren.push(...children); 88 const containerProperties = this.getContainerChildProperties( 89 containerChild, 90 children, 91 ); 92 return containerProperties; 93 }, 94 ), 95 ); 96 currChildren = nextChildren; 97 } 98 99 return containers; 100 } 101 102 private makeEntryEagerPropertiesTree( 103 entry: com.android.server.wm.IWindowManagerServiceDumpProto, 104 ): PropertyTreeNode { 105 const denyList: string[] = []; 106 const eagerProperties = assertDefined( 107 WM_EAGER_PROPERTIES.get(WmProtoType.WindowManagerService), 108 ); 109 let obj = entry; 110 do { 111 Object.getOwnPropertyNames(obj).forEach((it) => { 112 if (!eagerProperties.includes(it)) { 113 denyList.push(it); 114 } 115 }); 116 obj = Object.getPrototypeOf(obj); 117 } while (obj); 118 119 return new PropertyTreeBuilderFromProto() 120 .setData(entry) 121 .setRootId('WindowManagerState') 122 .setRootName('root') 123 .setDenyList(denyList) 124 .build(); 125 } 126 127 private makeEntryLazyPropertiesStrategy( 128 entry: com.android.server.wm.IWindowManagerServiceDumpProto, 129 ): LazyPropertiesStrategyType { 130 return async () => { 131 return new PropertyTreeBuilderFromProto() 132 .setData(entry) 133 .setRootId('WindowManagerState') 134 .setRootName('root') 135 .setDenyList( 136 assertDefined( 137 WM_DENYLIST_PROPERTIES.get(WmProtoType.WindowManagerService), 138 ), 139 ) 140 .build(); 141 }; 142 } 143 144 private getChildren( 145 child: com.android.server.wm.IWindowContainerChildProto, 146 ): com.android.server.wm.IWindowContainerChildProto[] { 147 let children: com.android.server.wm.IWindowContainerChildProto[] = []; 148 if (child.displayContent) { 149 children = 150 child.displayContent.rootDisplayArea?.windowContainer?.children ?? []; 151 } else if (child.displayArea) { 152 children = child.displayArea.windowContainer?.children ?? []; 153 } else if (child.task) { 154 const taskContainer = 155 child.task.taskFragment?.windowContainer ?? child.task.windowContainer; 156 children = taskContainer?.children ?? []; 157 } else if (child.taskFragment) { 158 children = child.taskFragment.windowContainer?.children ?? []; 159 } else if (child.activity) { 160 children = child.activity.windowToken?.windowContainer?.children ?? []; 161 } else if (child.windowToken) { 162 children = child.windowToken.windowContainer?.children ?? []; 163 } else if (child.window) { 164 children = child.window.windowContainer?.children ?? []; 165 } else if (child.windowContainer) { 166 children = child.windowContainer?.children ?? []; 167 } 168 169 return children; 170 } 171 172 private getContainerChildProperties( 173 containerChild: com.android.server.wm.IWindowContainerChildProto, 174 children: com.android.server.wm.IWindowContainerChildProto[], 175 operations?: OperationLists, 176 ): PropertiesProvider { 177 const containerChildType = this.getContainerChildType(containerChild); 178 179 const eagerProperties = this.makeContainerChildEagerPropertiesTree( 180 containerChild, 181 children, 182 containerChildType, 183 ); 184 const lazyPropertiesStrategy = 185 this.makeContainerChildLazyPropertiesStrategy( 186 containerChild, 187 containerChildType, 188 ); 189 190 if (!operations) { 191 operations = assertDefined(WM_OPERATION_LISTS.get(containerChildType)); 192 } 193 194 const containerProperties = new PropertiesProviderBuilder() 195 .setEagerProperties(eagerProperties) 196 .setLazyPropertiesStrategy(lazyPropertiesStrategy) 197 .setCommonOperations(operations.common) 198 .setEagerOperations(operations.eager) 199 .setLazyOperations(operations.lazy) 200 .build(); 201 return containerProperties; 202 } 203 204 private getContainerChildType( 205 child: com.android.server.wm.IWindowContainerChildProto, 206 ): WmProtoType { 207 if (child.displayContent) { 208 return WmProtoType.DisplayContent; 209 } else if (child.displayArea) { 210 return WmProtoType.DisplayArea; 211 } else if (child.task) { 212 return WmProtoType.Task; 213 } else if (child.taskFragment) { 214 return WmProtoType.TaskFragment; 215 } else if (child.activity) { 216 return WmProtoType.Activity; 217 } else if (child.windowToken) { 218 return WmProtoType.WindowToken; 219 } else if (child.window) { 220 return WmProtoType.WindowState; 221 } 222 223 return WmProtoType.WindowContainer; 224 } 225 226 private makeContainerChildEagerPropertiesTree( 227 containerChild: com.android.server.wm.IWindowContainerChildProto, 228 children: com.android.server.wm.IWindowContainerChildProto[], 229 containerChildType: WmProtoType, 230 ): PropertyTreeNode { 231 const identifier = this.getIdentifier(containerChild); 232 const name = this.getName(containerChild, identifier); 233 const token = this.makeToken(identifier); 234 235 const eagerProperties = assertDefined( 236 WM_EAGER_PROPERTIES.get(containerChildType), 237 ); 238 239 const denyList: string[] = []; 240 241 const container = this.getContainer(containerChild); 242 let obj = container; 243 do { 244 Object.getOwnPropertyNames(obj).forEach((it) => { 245 if (!eagerProperties.includes(it)) denyList.push(it); 246 }); 247 obj = Object.getPrototypeOf(obj); 248 } while (obj); 249 250 const containerProperties = new PropertyTreeBuilderFromProto() 251 .setData(container) 252 .setRootId(`${containerChildType} ${token}`) 253 .setRootName(name) 254 .setDenyList(denyList) 255 .build(); 256 257 if (children.length > 0) { 258 containerProperties.addOrReplaceChild( 259 DEFAULT_PROPERTY_TREE_NODE_FACTORY.makeCalculatedProperty( 260 containerProperties.id, 261 'children', 262 this.mapChildrenToTokens(children), 263 ), 264 ); 265 } 266 267 containerProperties.addOrReplaceChild( 268 DEFAULT_PROPERTY_TREE_NODE_FACTORY.makeCalculatedProperty( 269 containerProperties.id, 270 'token', 271 token, 272 ), 273 ); 274 275 return containerProperties; 276 } 277 278 private makeContainerChildLazyPropertiesStrategy( 279 containerChild: com.android.server.wm.IWindowContainerChildProto, 280 containerChildType: WmProtoType, 281 ): LazyPropertiesStrategyType { 282 return async () => { 283 const identifier = this.getIdentifier(containerChild); 284 const name = this.getName(containerChild, identifier); 285 const token = this.makeToken(identifier); 286 const containerDenylistProperties = assertDefined( 287 WM_DENYLIST_PROPERTIES.get(containerChildType), 288 ); 289 290 const container = this.getContainer(containerChild); 291 292 return new PropertyTreeBuilderFromProto() 293 .setData(container) 294 .setRootId(`${containerChildType} ${token}`) 295 .setRootName(name) 296 .setDenyList(containerDenylistProperties) 297 .build(); 298 }; 299 } 300 301 private getIdentifier( 302 child: com.android.server.wm.IWindowContainerChildProto, 303 ): com.android.server.wm.IIdentifierProto | undefined { 304 if (child.displayContent) { 305 return ( 306 child.displayContent.rootDisplayArea?.windowContainer?.identifier ?? 307 undefined 308 ); 309 } 310 if (child.displayArea) { 311 return child.displayArea.windowContainer?.identifier ?? undefined; 312 } 313 if (child.task) { 314 return ( 315 child.task.taskFragment?.windowContainer?.identifier ?? 316 child.task.windowContainer?.identifier ?? 317 undefined 318 ); 319 } 320 if (child.taskFragment) { 321 return child.taskFragment.windowContainer?.identifier ?? undefined; 322 } 323 if (child.activity) { 324 return ( 325 child.activity.identifier ?? 326 child.activity.windowToken?.windowContainer?.identifier ?? 327 undefined 328 ); 329 } 330 if (child.windowToken) { 331 return child.windowToken ?? undefined; 332 } 333 if (child.window) { 334 return ( 335 child.window.windowContainer?.identifier ?? 336 child.window.identifier ?? 337 undefined 338 ); 339 } 340 if (child.windowContainer) { 341 return child.windowContainer?.identifier ?? undefined; 342 } 343 return undefined; 344 } 345 346 private getName( 347 child: com.android.server.wm.IWindowContainerChildProto, 348 identifier: com.android.server.wm.IIdentifierProto | undefined, 349 ): string { 350 let nameOverride: string | undefined; 351 if (child.displayContent) { 352 nameOverride = child.displayContent.displayInfo?.name; 353 } else if (child.displayArea) { 354 nameOverride = child.displayArea.name ?? undefined; 355 } else if (child.activity) { 356 nameOverride = child.activity.name ?? undefined; 357 } else if (child.windowToken) { 358 nameOverride = child.windowToken.hashCode?.toString(16); 359 } else if (child.window) { 360 nameOverride = 361 child.window.windowContainer?.identifier?.title ?? 362 child.window.identifier?.title ?? 363 ''; 364 365 if (nameOverride.startsWith(WindowTypePrefix.STARTING)) { 366 nameOverride = nameOverride.substring(WindowTypePrefix.STARTING.length); 367 } else if (nameOverride.startsWith(WindowTypePrefix.DEBUGGER)) { 368 nameOverride = nameOverride.substring(WindowTypePrefix.DEBUGGER.length); 369 } 370 } 371 372 return nameOverride ?? identifier?.title ?? ''; 373 } 374 375 private makeToken( 376 identifier: com.android.server.wm.IIdentifierProto | undefined, 377 ): string { 378 return identifier?.hashCode?.toString(16) ?? ''; 379 } 380 381 private getContainer( 382 containerChild: com.android.server.wm.IWindowContainerChildProto, 383 ): WindowContainerChildType { 384 if (containerChild.displayContent) { 385 return containerChild.displayContent; 386 } 387 if (containerChild.displayArea) { 388 return containerChild.displayArea; 389 } 390 if (containerChild.task) { 391 return containerChild.task; 392 } 393 if (containerChild.activity) { 394 return containerChild.activity; 395 } 396 if (containerChild.windowToken) { 397 return containerChild.windowToken; 398 } 399 if (containerChild.window) { 400 return containerChild.window; 401 } 402 if (containerChild.taskFragment) { 403 return containerChild.taskFragment; 404 } 405 return assertDefined(containerChild.windowContainer); 406 } 407 408 private mapChildrenToTokens( 409 children: com.android.server.wm.IWindowContainerChildProto[], 410 ): string[] { 411 return children 412 .map((child) => { 413 const identifier = this.getIdentifier(child); 414 return this.makeToken(identifier); 415 }) 416 .filter((token) => token.length > 0); 417 } 418} 419 420export const ParserWmUtils = new ParserWindowManagerUtils(); 421