blob: c5062728cb2e168342264965f04c4dd9db52d0dd [file] [log] [blame]
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {assertDefined} from 'common/assert_utils';
import {Timestamp} from 'common/time';
import {TimeUtils} from 'common/time_utils';
import {Item} from 'trace/item';
import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
import {WindowType} from 'trace/window_type';
import {WmImeUtils} from 'viewers/common/wm_ime_utils';
import {TreeNodeFilter, UiTreeUtils} from './ui_tree_utils';
interface WmStateProperties {
timestamp: string | undefined;
focusedApp: string | undefined;
focusedWindow: string | undefined;
focusedActivity: string | undefined;
isInputMethodWindowVisible: boolean;
imeInputTarget: PropertyTreeNode | undefined;
imeLayeringTarget: PropertyTreeNode | undefined;
imeInsetsSourceProvider: PropertyTreeNode | undefined;
imeControlTarget: PropertyTreeNode | undefined;
}
export class ProcessedWindowManagerState implements Item {
constructor(
readonly id: string,
readonly name: string,
readonly wmStateProperties: WmStateProperties,
readonly hierarchyTree: HierarchyTreeNode
) {}
}
interface ImeContainerProperties {
id: string;
zOrderRelativeOfId: number;
z: number;
}
interface InputMethodSurfaceProperties {
id: string;
isVisible: boolean;
screenBounds?: PropertyTreeNode;
rect?: PropertyTreeNode;
}
interface RootImeProperties {
timestamp: string;
}
interface ImeLayerProperties {
imeContainer: ImeContainerProperties | undefined;
inputMethodSurface: InputMethodSurfaceProperties | undefined;
focusedWindowColor: PropertyTreeNode | undefined;
root: RootImeProperties | undefined;
}
export class ImeLayers implements Item {
constructor(
readonly id: string,
readonly name: string,
readonly properties: ImeLayerProperties,
readonly taskLayerOfImeContainer: HierarchyTreeNode | undefined,
readonly taskLayerOfImeSnapshot: HierarchyTreeNode | undefined
) {}
}
class ImeAdditionalPropertiesUtils {
processWindowManagerTraceEntry(
entry: HierarchyTreeNode,
wmEntryTimestamp: Timestamp | undefined
): ProcessedWindowManagerState {
const displayContent = entry.getAllChildren()[0];
const props: WmStateProperties = {
timestamp: wmEntryTimestamp ? TimeUtils.format(wmEntryTimestamp) : undefined,
focusedApp: entry.getEagerPropertyByName('focusedApp')?.getValue(),
focusedWindow: this.getFocusedWindowString(entry),
focusedActivity: this.getFocusedActivityString(entry),
isInputMethodWindowVisible: this.isInputMethodVisible(displayContent),
imeInputTarget: this.getImeInputTargetProperty(displayContent),
imeLayeringTarget: this.getImeLayeringTargetProperty(displayContent),
imeInsetsSourceProvider: displayContent.getEagerPropertyByName('imeInsetsSourceProvider'),
imeControlTarget: this.getImeControlTargetProperty(displayContent),
};
return new ProcessedWindowManagerState(entry.id, entry.name, props, entry);
}
getImeLayers(
entryTree: HierarchyTreeNode,
processedWindowManagerState: ProcessedWindowManagerState,
sfEntryTimestamp: Timestamp | undefined
): ImeLayers | undefined {
const isImeContainer = UiTreeUtils.makeNodeFilter('ImeContainer');
const imeContainerLayer = entryTree.findDfs(isImeContainer);
if (!imeContainerLayer) {
return undefined;
}
const imeContainerProps: ImeContainerProperties = {
id: imeContainerLayer.id,
zOrderRelativeOfId: assertDefined(
imeContainerLayer.getEagerPropertyByName('zOrderRelativeOf')
).getValue(),
z: assertDefined(imeContainerLayer.getEagerPropertyByName('z')).getValue(),
};
const isInputMethodSurface = UiTreeUtils.makeNodeFilter('InputMethod');
const inputMethodSurfaceLayer = imeContainerLayer.findDfs(isInputMethodSurface);
if (!inputMethodSurfaceLayer) {
return undefined;
}
const inputMethodSurfaceProps: InputMethodSurfaceProperties = {
id: inputMethodSurfaceLayer.id,
isVisible: assertDefined(
inputMethodSurfaceLayer.getEagerPropertyByName('isComputedVisible')
).getValue(),
screenBounds: inputMethodSurfaceLayer.getEagerPropertyByName('screenBounds'),
rect: inputMethodSurfaceLayer.getEagerPropertyByName('bounds'),
};
let focusedWindowLayer: HierarchyTreeNode | undefined;
const focusedWindowToken = processedWindowManagerState.wmStateProperties.focusedWindow
?.split(' ')[0]
.slice(1);
if (focusedWindowToken) {
const isFocusedWindow = UiTreeUtils.makeNodeFilter(focusedWindowToken);
focusedWindowLayer = entryTree.findDfs(isFocusedWindow);
}
const focusedWindowColor = focusedWindowLayer
? focusedWindowLayer.getEagerPropertyByName('color')
: undefined;
// we want to see both ImeContainer and IME-snapshot if there are
// cases where both exist
const taskLayerOfImeContainer = this.findAncestorTaskLayerOfImeLayer(
entryTree,
UiTreeUtils.makeNodeFilter('ImeContainer')
);
const taskLayerOfImeSnapshot = this.findAncestorTaskLayerOfImeLayer(
entryTree,
UiTreeUtils.makeNodeFilter('IME-snapshot')
);
const rootProperties = sfEntryTimestamp
? {timestamp: TimeUtils.format(sfEntryTimestamp)}
: undefined;
return new ImeLayers(
entryTree.id,
entryTree.name,
{
imeContainer: imeContainerProps,
inputMethodSurface: inputMethodSurfaceProps,
focusedWindowColor,
root: rootProperties,
},
taskLayerOfImeContainer,
taskLayerOfImeSnapshot
);
}
private getFocusedWindowString(entry: HierarchyTreeNode): string | undefined {
let focusedWindowString = undefined;
const focusedWindow = WmImeUtils.getFocusedWindow(entry);
if (focusedWindow) {
const token = assertDefined(focusedWindow.getEagerPropertyByName('token')).getValue();
const windowTypeSuffix = this.getWindowTypeSuffix(
assertDefined(focusedWindow.getEagerPropertyByName('windowType')).getValue()
);
const type = assertDefined(
focusedWindow.getEagerPropertyByName('attributes')?.getChildByName('type')
).formattedValue();
const windowFrames = assertDefined(focusedWindow.getEagerPropertyByName('windowFrames'));
const containingFrame = assertDefined(
windowFrames.getChildByName('containingFrame')?.formattedValue()
);
const parentFrame = assertDefined(
windowFrames.getChildByName('parentFrame')?.formattedValue()
);
focusedWindowString = `{${token} ${focusedWindow.name}${windowTypeSuffix}} type=${type} cf=${containingFrame} pf=${parentFrame}`;
}
return focusedWindowString;
}
private getFocusedActivityString(entry: HierarchyTreeNode): string {
let focusedActivityString = 'null';
const focusedActivity = WmImeUtils.getFocusedActivity(entry);
if (focusedActivity) {
const token = assertDefined(focusedActivity.getEagerPropertyByName('token')).getValue();
const state = assertDefined(focusedActivity.getEagerPropertyByName('state')).getValue();
const isVisible =
focusedActivity.getEagerPropertyByName('isComputedVisible')?.getValue() ?? false;
focusedActivityString = `{${token} ${focusedActivity.name}} state=${state} visible=${isVisible}`;
}
return focusedActivityString;
}
private getWindowTypeSuffix(windowType: number): string {
switch (windowType) {
case WindowType.STARTING:
return ' STARTING';
case WindowType.EXITING:
return ' EXITING';
case WindowType.DEBUGGER:
return ' DEBUGGER';
default:
return '';
}
}
private findAncestorTaskLayerOfImeLayer(
entryTree: HierarchyTreeNode,
isTargetImeLayer: TreeNodeFilter
): HierarchyTreeNode | undefined {
const imeLayer = entryTree.findDfs(isTargetImeLayer);
if (!imeLayer) {
return undefined;
}
const isTaskLayer = UiTreeUtils.makeNodeFilter('Task|ImePlaceholder');
const taskLayer = imeLayer.findAncestor(isTaskLayer);
if (!taskLayer) {
return undefined;
}
return taskLayer;
}
private getImeControlTargetProperty(
displayContent: HierarchyTreeNode
): PropertyTreeNode | undefined {
return displayContent.getEagerPropertyByName('inputMethodControlTarget');
}
private getImeInputTargetProperty(
displayContent: HierarchyTreeNode
): PropertyTreeNode | undefined {
return displayContent.getEagerPropertyByName('inputMethodInputTarget');
}
private getImeLayeringTargetProperty(
displayContent: HierarchyTreeNode
): PropertyTreeNode | undefined {
return displayContent.getEagerPropertyByName('inputMethodTarget');
}
private isInputMethodVisible(displayContent: HierarchyTreeNode): boolean {
const isInputMethod = UiTreeUtils.makeNodeFilter('InputMethod');
const inputMethodWindowOrLayer = displayContent.findDfs(isInputMethod);
return inputMethodWindowOrLayer?.getEagerPropertyByName('isComputedVisible')?.getValue();
}
}
export const ImeUtils = new ImeAdditionalPropertiesUtils();