Make winscope compatible with flicker/CTS infra

Flickerlib is migrating to the same structure as the CTS tests. This cl
makes winscope compatible with this changes. It now parses traces with
the new information and add a "flickerObj" entry to each node with the
calculated vlaues from flickerlib

Test: yarn run dev and open a few WM traces
Bug: 167521440
Bug: 170372278
Change-Id: I4a116ccabe392c75e5b5049808d4837f865cb2c7
This commit is contained in:
Nataniel Borges
2020-10-15 12:22:03 +02:00
parent c1b2262941
commit 5e4ca356ae
19 changed files with 577 additions and 575 deletions

View File

@@ -252,7 +252,8 @@ export default {
this.item = item; this.item = item;
this.tree = this.generateTreeFromItem(item); this.tree = this.generateTreeFromItem(item);
this.rects = [...item.rects].reverse(); const rects = item.rects //.toArray()
this.rects = [...rects].reverse();
this.bounds = item.bounds; this.bounds = item.bounds;
this.hierarchySelected = null; this.hierarchySelected = null;

View File

@@ -17,7 +17,7 @@
import { FILE_TYPES, DUMP_TYPES } from "@/decode.js"; import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
import DumpBase from "./DumpBase"; import DumpBase from "./DumpBase";
import { WindowManagerTraceEntry } from '@/flickerlib'; import { WindowManagerTrace } from '@/flickerlib';
export default class WindowManager extends DumpBase { export default class WindowManager extends DumpBase {
wmDumpFile: any; wmDumpFile: any;
@@ -32,7 +32,7 @@ export default class WindowManager extends DumpBase {
return DUMP_TYPES.WINDOW_MANAGER; return DUMP_TYPES.WINDOW_MANAGER;
} }
static fromProto(proto): WindowManagerTraceEntry { static fromProto(proto): WindowManagerTrace {
return WindowManagerTraceEntry.fromProto(proto); return WindowManagerTrace.fromDump(proto);
} }
} }

View File

@@ -0,0 +1,110 @@
/*
* Copyright 2020, 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 { nanosToString, TimeUnits } from "../utils/utils.js"
import { getWMPropertiesForDisplay } from './mixin'
import {
KeyguardControllerState,
RootWindowContainer,
WindowManagerPolicy,
WindowManagerState
} from "./common"
import WindowContainer from "./windows/WindowContainer"
WindowManagerState.fromProto = function ({proto, timestamp = 0, where = ""}): WindowManagerState {
var inputMethodWIndowAppToken = ""
if (proto.inputMethodWindow != null) {
proto.inputMethodWindow.hashCode.toString(16)
}
const rootWindowContainer = newRootWindowContainer(proto.rootWindowContainer)
const keyguardControllerState = newKeyguardControllerState(
proto.rootWindowContainer.keyguardController)
const entry = new WindowManagerState(
where,
newWindowManagerPolicy(proto.policy),
proto.focusedApp,
proto.focusedDisplayId,
proto.focusedWindow?.title ?? "",
inputMethodWIndowAppToken,
proto.rootWindowContainer.isHomeRecentsComponent,
proto.displayFrozen,
proto.rotation,
proto.lastOrientation,
proto.rootWindowContainer.pendingActivities.map(it => it.title),
rootWindowContainer,
keyguardControllerState,
timestamp = timestamp
)
entry.obj = getWMPropertiesForDisplay(proto)
entry.name = nanosToString(entry.timestamp, TimeUnits.MILLI_SECONDS)
entry.shortName = entry.name
entry.children = entry.root.childrenWindows.reverse()
entry.chips = []
entry.visible = true
return entry
}
function newWindowManagerPolicy(proto): WindowManagerPolicy {
return new WindowManagerPolicy(
proto.focusedAppToken || "",
proto.forceStatusBar,
proto.forceStatusBarFromKeyguard,
proto.keyguardDrawComplete,
proto.keyguardOccluded,
proto.keyguardOccludedChanged,
proto.keyguardOccludedPending,
proto.lastSystemUiFlags,
proto.orientation,
proto.rotation,
proto.rotationMode,
proto.screenOnFully,
proto.windowManagerDrawComplete
)
}
function newRootWindowContainer(proto): RootWindowContainer {
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new RootWindowContainer(windowContainer)
proto.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, /* isActivityInTree */ false))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
return entry
}
function newKeyguardControllerState(proto): KeyguardControllerState {
const keyguardOccludedStates = {}
if (proto) {
proto.keyguardOccludedStates.forEach(it =>
keyguardOccludedStates[it.displayId] = it.keyguardOccluded)
}
return new KeyguardControllerState(
proto?.aodShowing ?? false,
proto?.keyguardShowing ?? false,
keyguardOccludedStates
)
}
export default WindowManagerState;

View File

@@ -14,23 +14,26 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { WindowManagerTrace } from "./common"
WindowManagerTrace, import WindowManagerState from "./WindowManagerState"
} from "./common"
import WindowManagerTraceEntry from "./WindowManagerTraceEntry"
WindowManagerTrace.fromProto = function (proto) { WindowManagerTrace.fromProto = function (proto) {
const entries = [] const entries = []
for (const entryProto of proto.entry) { for (const entryProto of proto.entry) {
const transformedEntry = WindowManagerTraceEntry.fromProto( const transformedEntry = WindowManagerState.fromProto({
entryProto.windowManagerService, entryProto.elapsedRealtimeNanos) proto: entryProto.windowManagerService,
timestamp: entryProto.elapsedRealtimeNanos,
where: entryProto.where})
entries.push(transformedEntry) entries.push(transformedEntry)
}
const source = null; }
const sourceChecksum = null; const source = null
const sourceChecksum = null
return new WindowManagerTrace(entries, source, sourceChecksum) return new WindowManagerTrace(entries, source, sourceChecksum)
} }
WindowManagerTrace.fromDump = function(proto): WindowManagerTrace {
return WindowManagerState.fromProto({proto: proto})
}
export default WindowManagerTrace; export default WindowManagerTrace;

View File

@@ -1,113 +0,0 @@
/*
* Copyright 2020, 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 { nanosToString, TimeUnits } from "../utils/utils.js"
import { WindowManagerTraceEntry } from "./common"
import { applyMixins } from "./mixin"
import ITreeViewElement from './treeview/IClickableTreeViewElement'
import IClickableTreeViewElement from './treeview/IClickableTreeViewElement'
import Chip from './treeview/Chip'
import WindowContainer from "./windows/WindowContainer"
class WindowManagerTraceEntryMixin implements IClickableTreeViewElement {
common: any
kind: string
name: string
shortName: string
stableId: string
chips: Array<Chip>
children: Array<ITreeViewElement>
obj: any
rawTreeViewObject
timestamp: number
rootWindow
mixinConstructor(obj) {
const name = this.timestamp ? nanosToString(this.timestamp, TimeUnits.MILLI_SECONDS) : ""
this.kind = "entry"
this.name = name
this.shortName = name
this.stableId = "entry"
this.children = this.rootWindow.children
this.chips = []
this.obj = obj
}
static fromProto(proto, timestamp) {
const rootWindow =
WindowContainer.fromProto(proto.rootWindowContainer.windowContainer, null)
const windowManagerTraceEntry =
new WindowManagerTraceEntry(rootWindow, timestamp)
windowManagerTraceEntry.kind = 'service'
windowManagerTraceEntry.focusedApp = proto.focusedApp
windowManagerTraceEntry.focusedDisplayId = proto.focusedDisplayId
windowManagerTraceEntry.lastOrientation = proto.lastOrientation
windowManagerTraceEntry.policy = proto.policy
windowManagerTraceEntry.rotation = proto.rotation
windowManagerTraceEntry.displayFrozen = proto.displayFrozen
windowManagerTraceEntry.inputMethodWindow = proto.inputMethodWindow
// Remove anything that is part of the children elements
// allows for faster loading of properties and less information cluttering
// this applied to anywhere the proto is passed to be saved as .obj
const obj = Object.assign({}, proto)
obj.rootWindowContainer = {};
Object.assign(obj.rootWindowContainer,
proto.rootWindowContainer)
obj.rootWindowContainer.windowContainer = {};
Object.assign(obj.rootWindowContainer.windowContainer,
proto.rootWindowContainer.windowContainer)
delete obj.rootWindowContainer.windowContainer.children
windowManagerTraceEntry.mixinConstructor(obj)
return windowManagerTraceEntry
}
attachObject(obj) {
this.obj = obj
}
asRawTreeViewObject() {
// IMPORTANT: We want to always return the same tree view object and not
// generate a new one every time this function is called.
if (!this.rawTreeViewObject) {
const children = this.children.map(child => child.asRawTreeViewObject())
this.rawTreeViewObject = {
kind: this.kind,
name: this.name,
shortName: this.shortName,
stableId: this.stableId,
chips: this.chips,
obj: this.obj,
children,
ref: this,
}
}
return this.rawTreeViewObject;
}
}
applyMixins(WindowManagerTraceEntry, [WindowManagerTraceEntryMixin])
export default WindowManagerTraceEntry;

View File

@@ -17,42 +17,69 @@
// Imports all the compiled common Flicker library classes and exports them // Imports all the compiled common Flicker library classes and exports them
// as clean es6 modules rather than having them be commonjs modules // as clean es6 modules rather than having them be commonjs modules
const WindowManagerTrace = require('flicker').com.android.server.wm.flicker const WindowManagerTrace = require('flicker').com.android.server.wm.flicker.
.common.traces.windowmanager.WindowManagerTrace; common.traces.windowmanager.WindowManagerTrace;
const WindowManagerTraceEntry = require('flicker').com.android.server.wm. const WindowManagerState = require('flicker').com.android.server.wm.
flicker.common.traces.windowmanager.WindowManagerTraceEntry; flicker.common.traces.windowmanager.WindowManagerState;
const WindowContainer = require('flicker').com.android.server.wm.flicker.common. const Activity = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowContainer; traces.windowmanager.windows.Activity;
const WindowState = require('flicker').com.android.server.wm.flicker.common. const ActivityTask = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowState; traces.windowmanager.windows.ActivityTask;
const DisplayContent = require('flicker').com.android.server.wm.flicker.common. const Configuration = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.DisplayContent; traces.windowmanager.windows.Configuration;
const ActivityRecord = require('flicker').com.android.server.wm.flicker.common. const ConfigurationContainer = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.ActivityRecord; traces.windowmanager.windows.ConfigurationContainer;
const WindowToken = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowToken;
const DisplayArea = require('flicker').com.android.server.wm.flicker.common. const DisplayArea = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.DisplayArea; traces.windowmanager.windows.DisplayArea;
const RootDisplayArea = require('flicker').com.android.server.wm.flicker.common. const DisplayContent = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.RootDisplayArea; traces.windowmanager.windows.DisplayContent;
const Task = require('flicker').com.android.server.wm.flicker.common.traces. const KeyguardControllerState = require('flicker').com.android.server.wm.flicker.common.
windowmanager.windows.Task; traces.windowmanager.windows.KeyguardControllerState;
const RootWindowContainer = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.RootWindowContainer;
const WindowConfiguration = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowConfiguration;
const WindowContainer = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowContainer;
const WindowContainerChild = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowContainerChild;
const WindowManagerPolicy = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowManagerPolicy;
const WindowState = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowState;
const WindowToken = require('flicker').com.android.server.wm.flicker.common.
traces.windowmanager.windows.WindowToken;
const Rect = require('flicker').com.android.server.wm.flicker.common.Rect; const Rect = require('flicker').com.android.server.wm.flicker.common.Rect;
const Bounds = require('flicker').com.android.server.wm.flicker.common.Bounds; const Bounds = require('flicker').com.android.server.wm.flicker.common.Bounds;
function toRect(proto) {
if (proto == null) {
return new Rect(0, 0, 0, 0)
} else {
return new Rect(proto.left, proto.top, proto.right, proto.bottom)
}
}
export { export {
WindowManagerTrace, Activity,
WindowManagerTraceEntry, ActivityTask,
WindowContainer, Configuration,
WindowState, ConfigurationContainer,
DisplayContent,
ActivityRecord,
WindowToken,
DisplayArea, DisplayArea,
RootDisplayArea, DisplayContent,
Task, KeyguardControllerState,
RootWindowContainer,
WindowConfiguration,
WindowContainer,
WindowContainerChild,
WindowState,
WindowToken,
WindowManagerPolicy,
WindowManagerTrace,
WindowManagerState,
Rect, Rect,
Bounds, Bounds,
toRect
}; };

View File

@@ -14,12 +14,12 @@
* limitations under the License. * limitations under the License.
*/ */
import WindowManagerTraceEntry from './WindowManagerTraceEntry'; import WindowManagerState from './WindowManagerState';
import WindowManagerTrace from './WindowManagerTrace'; import WindowManagerTrace from './WindowManagerTrace';
/** /**
* Entry point into the flickerlib for Winscope. * Entry point into the flickerlib for Winscope.
* Expose everything we want Winscope to have access to here. * Expose everything we want Winscope to have access to here.
*/ */
export {WindowManagerTraceEntry, WindowManagerTrace}; export {WindowManagerState, WindowManagerTrace};

View File

@@ -1,23 +1,44 @@
/** /*
* Injects all the properties (getters, setters, functions...) of a list of * Copyright 2020, The Android Open Source Project
* classes (baseCtors) into a class (derivedCtor). *
* @param derivedCtor The constructor of the class we want to inject the * Licensed under the Apache License, Version 2.0 (the "License");
* properties into. * you may not use this file except in compliance with the License.
* @param baseCtors A list of consturctor of the classes we want to mixin the * You may obtain a copy of the License at
* properties of into the derivedCtor. *
* 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.
*/ */
export function applyMixins(derivedCtor: any, baseCtors: any[]) {
baseCtors.forEach(baseCtor => {
Object.getOwnPropertyNames(baseCtor).forEach(name => {
if (['length', 'name', 'prototype'].includes(name)) {
return;
}
Object.defineProperty(derivedCtor, name, Object.getOwnPropertyDescriptor(baseCtor, name)) /**
}) * Get the properties of a WM object for display.
*
Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => { * @param entry WM hierarchy element
Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name)) * @param proto Associated proto object
}) */
}); export function getWMPropertiesForDisplay(proto: any): any {
const obj = Object.assign({}, proto)
if (obj.children) delete obj.children
if (obj.childWindows) delete obj.childWindows
if (obj.childrenWindows) delete obj.childrenWindows
if (obj.childContainers) delete obj.childContainers
if (obj.identifier) delete obj.identifier
if (obj.windowToken) delete obj.windowToken
if (obj.rootDisplayArea) delete obj.rootDisplayArea
if (obj.rootWindowContainer) delete obj.rootWindowContainer
if (obj.windowContainer) delete obj.windowContainer
return obj
}
export function shortenName(name: any): string {
const classParts = (name + "").split(".")
if (classParts.length <= 3) {
return name
}
const className = classParts.slice(-1)[0] // last element
return `${classParts[0]}.${classParts[1]}.(...).${className}`
} }

View File

@@ -0,0 +1,53 @@
/*
* Copyright 2020, 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 { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { Activity } from "../common"
import WindowContainer from "./WindowContainer"
Activity.fromProto = function (proto, parent: WindowContainer): Activity {
if (proto == null) {
return null
} else {
const windowContainer = WindowContainer.fromProto({proto: proto.windowToken.windowContainer,
identifierOverride: proto.identifier})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new Activity(
proto.name,
proto.state,
proto.visible,
proto.frontOfTask,
proto.procId,
proto.translucent,
parent,
windowContainer
)
proto.windowToken.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, /* isActivityInTree */ true))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
entry.children = entry.childrenWindows
return entry
}
}
export default Activity

View File

@@ -1,46 +0,0 @@
/*
* Copyright 2020, 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 {
ActivityRecord,
} from "../common"
import { applyMixins } from '../mixin'
import WindowToken from "./WindowToken"
export class ActivityRecordMixin {
get kind() {
return "DisplayArea"
}
static fromProto(proto) {
const windowToken = WindowToken.fromProto(proto.windowToken)
const activityRecord = new ActivityRecord(windowToken)
const obj = Object.assign({}, proto)
delete proto.windowToken
Object.assign(obj, windowToken.obj)
activityRecord.attachObject(obj)
return activityRecord
}
}
applyMixins(ActivityRecord, [ActivityRecordMixin])
export default ActivityRecord

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2020, 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 { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { ActivityTask, toRect } from "../common"
import WindowContainer from "./WindowContainer"
ActivityTask.fromProto = function (proto, isActivityInTree: Boolean): ActivityTask {
if (proto == null) {
return null
} else {
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new ActivityTask(
proto.activityType,
proto.fillsParent,
toRect(proto.bounds),
proto.id,
proto.rootTaskId,
proto.displayId,
toRect(proto.lastNonFullscreenBounds),
proto.realActivity,
proto.origActivity,
proto.resizeMode,
proto.resumedActivity?.title ?? "",
proto.animatingBounds,
proto.surfaceWidth,
proto.surfaceHeight,
proto.createdByOrganizer,
proto.minWidth,
proto.minHeight,
windowContainer
)
proto.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
entry.children = entry.childrenWindows
return entry
}
}
export default ActivityTask

View File

@@ -14,34 +14,30 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { getWMPropertiesForDisplay, shortenName } from '../mixin'
DisplayArea, import { DisplayArea } from "../common"
} from "../common"
import { applyMixins } from '../mixin'
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
export class DisplayAreaMixin { DisplayArea.fromProto = function (proto, isActivityInTree: Boolean): DisplayArea {
get kind() { if (proto == null) {
return "DisplayArea" return null
} else {
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer, nameOverride: proto.name})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
} }
const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer)
static fromProto(proto) { proto.windowContainer.children.reverse()
const windowContainer = WindowContainer.fromProto(proto.windowContainer, .map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
null) .filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
const displayArea = new DisplayArea(windowContainer) entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
const obj = Object.assign({}, proto) entry.children = entry.childrenWindows
delete obj.windowContainer return entry
Object.assign(obj, windowContainer.obj)
displayArea.attachObject(obj)
return displayArea
} }
} }
applyMixins(DisplayArea, [DisplayAreaMixin])
export default DisplayArea export default DisplayArea

View File

@@ -14,50 +14,57 @@
* limitations under the License. * limitations under the License.
*/ */
import { import { getWMPropertiesForDisplay, shortenName } from '../mixin'
DisplayContent, import { toRect, DisplayContent, Rect } from "../common"
Bounds
} from "../common"
import { applyMixins } from '../mixin'
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
import DisplayArea from "./DisplayArea"
export class DisplayContentMixin { DisplayContent.fromProto = function (proto, isActivityInTree: Boolean): DisplayContent {
get kind() { if (proto == null) {
return "DisplayContent" return null
}
static fromProto(proto) {
let rootDisplayArea;
if (proto.rootDisplayArea.windowContainer == null) {
// For backward compatibility
const windowContainer = WindowContainer.fromProto(proto.windowContainer,
null)
rootDisplayArea = new DisplayArea(windowContainer)
} else { } else {
// New protos should always be using this const windowContainer = WindowContainer.fromProto({proto: proto.rootDisplayArea.windowContainer,
rootDisplayArea = DisplayArea.fromProto(proto.rootDisplayArea) nameOverride: proto.displayInfo?.name ?? null})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
} }
const bounds = new Bounds( const displayRectWidth = proto.displayInfo?.logicalWidth ?? 0
proto.displayInfo.logicalWidth || 0, const displayRectHeight = proto.displayInfo?.logicalHeight ?? 0
proto.displayInfo.logicalHeight || 0, const appRectWidth = proto.displayInfo?.appWidth ?? 0
const appRectHeight = proto.displayInfo?.appHeight ?? 0
const defaultBounds = proto.pinnedStackController?.defaultBounds ?? null
const movementBounds = proto.pinnedStackController?.movementBounds ?? null
const entry = new DisplayContent(
proto.id,
proto.focusedRootTaskId,
proto.resumedActivity?.title ?? "",
proto.singleTaskInstance,
toRect(defaultBounds),
toRect(movementBounds),
new Rect(0, 0, displayRectWidth, displayRectHeight),
new Rect(0, 0, appRectWidth, appRectHeight),
proto.dpi,
proto.displayInfo?.flags ?? 0,
toRect(proto.displayFrames.stableBounds),
proto.surfaceSize,
proto.focusedApp,
proto.appTransition?.lastUsedAppTransition ?? "",
proto.appTransition?.appTransitionState ?? "",
windowContainer
) )
const displayContent = new DisplayContent(rootDisplayArea, bounds) proto.rootDisplayArea.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
const obj = Object.assign({}, proto) entry.obj = getWMPropertiesForDisplay(proto)
delete obj.windowContainer entry.shortName = shortenName(entry.name)
delete obj.rootDisplayArea entry.children = entry.childrenWindows
Object.assign(obj, rootDisplayArea.obj) return entry
displayContent.attachObject(obj)
return displayContent
} }
} }
applyMixins(DisplayContent, [DisplayContentMixin])
export default DisplayContent export default DisplayContent

View File

@@ -1,22 +0,0 @@
/*
* Copyright 2020, 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 { RootDisplayArea } from "../common"
import DisplayArea from "./DisplayArea"
RootDisplayArea.fromProto = DisplayArea.fromProto
export default DisplayArea

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2020, 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 { Task } from "../common"
import { applyMixins } from '../mixin'
import WindowContainer from "./WindowContainer"
export class TaskMixin {
get kind() {
return "Task"
}
static fromProto(proto) {
const windowContainer = WindowContainer.fromProto(proto.windowContainer,
null)
const task = new Task(windowContainer)
const obj = Object.assign({}, proto)
delete obj.windowContainer
Object.assign(obj, windowContainer.obj)
task.attachObject(obj)
return task
}
}
applyMixins(Task, [TaskMixin])
export default Task

View File

@@ -14,192 +14,105 @@
* limitations under the License. * limitations under the License.
*/ */
import { WindowContainer } from "../common" import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { applyMixins } from '../mixin' import {
Configuration,
import IClickableTreeViewElement from '../treeview/IClickableTreeViewElement' ConfigurationContainer,
import { TreeViewObject } from '../treeview/types' toRect,
WindowConfiguration,
WindowContainer,
WindowContainerChild,
} from "../common"
import Activity from "./Activity"
import DisplayArea from "./DisplayArea" import DisplayArea from "./DisplayArea"
import DisplayContent from "./DisplayContent" import DisplayContent from "./DisplayContent"
import Task from "./Task" import ActivityTask from "./ActivityTask"
import ActivityRecord from "./ActivityRecord"
import WindowToken from "./WindowToken"
import WindowState from "./WindowState" import WindowState from "./WindowState"
import WindowToken from "./WindowToken"
import { CompatibleFeatures } from '@/utils/compatibility.js' WindowContainer.fromProto = function ({
proto,
export class WindowContainerMixin implements IClickableTreeViewElement { nameOverride = null,
title: string identifierOverride = null,
windowHashCode: number tokenOverride = null
childrenWindows }): WindowContainer {
visible: boolean if (proto == null) {
rawTreeViewObject
_obj: { name: string }
_children
get kind() {
return "WindowContainer"
}
get name() {
if (this.obj && this.obj.name) {
return this.obj.name
}
if (["WindowContainer", "Task"].includes(this.title)) {
return null return null
} } else {
const identifier = identifierOverride ?? proto.identifier
var name = nameOverride ?? identifier?.title ?? ""
var token = tokenOverride?.toString(16) ?? identifier?.hashCode?.toString(16) ?? ""
return `${removeRedundancyInName(this.title)}@${this.windowHashCode}` const entry = new WindowContainer(
} name,
token,
proto.orientation,
proto.visible,
newConfigurationContainer(proto.configurationContainer)
)
get shortName() {
return this.name ? shortenName(this.name) : null
}
get stableId() {
return this.windowHashCode
}
get children() {
return this._children ?? this.childrenWindows
}
set children(children) {
this._children = children
}
get chips() {
return []
}
set obj(obj) {
this._obj = obj
}
get obj() {
return this._obj
}
static fromProto(proto, parent_identifier) {
const children = proto.children.map(
child => transformWindowContainerChildProto(child))
// A kind of hacky way to check if the proto is from a device which
// didn't have the changes required for the diff viewer to work properly
// We required that all elements have a stable identifier in order to do the
// diff properly. In theory properties diff would still work, but are
// currently disabled. If required in the future don't hesitate to make
// the required changes.
if (!proto.identifier) {
CompatibleFeatures.DiffVisualization = false;
}
const fallback_title = parent_identifier?.title ?? ""
const fallback_hashCode = parent_identifier?.hashCode ?? ""
const title = proto.identifier?.title ?? fallback_title
const hashCode = proto.identifier?.hashCode ?? fallback_hashCode
const visible = proto.visible
const windowContainer =
new WindowContainer(children, title, hashCode, visible)
const obj = Object.assign({}, proto)
// we remove the children property from the object to avoid it showing the // we remove the children property from the object to avoid it showing the
// the properties view of the element as we can always see those elements' // the properties view of the element as we can always see those elements'
// properties by changing the target element in the hierarchy tree view. // properties by changing the target element in the hierarchy tree view.
delete obj.children entry.obj = getWMPropertiesForDisplay(proto)
windowContainer.attachObject(obj) entry.shortName = shortenName(entry.name)
return entry
return windowContainer
}
attachObject(obj) {
this._obj = obj
}
asRawTreeViewObject(): TreeViewObject {
// IMPORTANT: We want to always return the same tree view object and not
// generate a new one every time this function is called.
if (!this.rawTreeViewObject) {
const children = this.children.map(child => child.asRawTreeViewObject())
this.rawTreeViewObject = {
kind: this.kind,
name: this.name,
shortName: this.shortName,
stableId: this.stableId,
chips: this.chips,
obj: this.obj,
children,
ref: this,
};
}
return this.rawTreeViewObject;
} }
} }
applyMixins(WindowContainer, [WindowContainerMixin]) WindowContainer.childrenFromProto = function(parent: WindowContainer, proto, isActivityInTree: Boolean): WindowContainerChild {
return new WindowContainerChild(
function transformWindowContainerChildProto(proto) { DisplayContent.fromProto(proto.displayContent, isActivityInTree),
if (proto.displayArea != null) { DisplayArea.fromProto(proto.displayArea, isActivityInTree),
return DisplayArea.fromProto(proto.displayArea) ActivityTask.fromProto(proto.task, isActivityInTree),
} Activity.fromProto(proto.activity, parent),
WindowToken.fromProto(proto.windowToken, isActivityInTree),
if (proto.displayContent != null) { WindowState.fromProto(proto.window, isActivityInTree),
return DisplayContent.fromProto(proto.displayContent) WindowContainer.fromProto({proto: proto.windowContainer})
} )
if (proto.task != null) {
return Task.fromProto(proto.task)
}
if (proto.activity != null) {
return ActivityRecord.fromProto(proto.activity)
}
if (proto.windowToken != null) {
return WindowToken.fromProto(proto.windowToken)
}
if (proto.window != null) {
return WindowState.fromProto(proto.window)
}
throw new Error("Unhandled WindowContainerChildProto case...")
} }
function removeRedundancyInName(name: string): string { function newConfigurationContainer(proto): ConfigurationContainer {
if (!name.includes('/')) { const entry = new ConfigurationContainer(
return name newConfiguration(proto?.overrideConfiguration ?? null),
} newConfiguration(proto?.fullConfiguration ?? null),
newConfiguration(proto?.mergedOverrideConfiguration ?? null)
)
const split = name.split('/') entry.kind = "ConfigurationContainer"
const pkg = split[0] entry.obj = entry
var clazz = split.slice(1).join("/") return entry
if (clazz.startsWith("$pkg.")) {
clazz = clazz.slice(pkg.length + 1)
return "$pkg/$clazz"
}
return name
} }
function shortenName(name: string): string { function newConfiguration(proto): Configuration {
const classParts = name.split(".") var windowConfiguration = null
if (classParts.length <= 3) { if (proto != null && proto.windowConfiguration != null) {
return name windowConfiguration = newWindowConfiguration(proto.windowConfiguration)
} }
const className = classParts.slice(-1)[0] // last element return new Configuration(
windowConfiguration,
proto?.densityDpi ?? 0,
proto?.orientation ?? 0,
proto?.screenHeightDp ?? 0,
proto?.screenHeightDp ?? 0,
proto?.smallestScreenWidthDp ?? 0,
proto?.screenLayout ?? 0,
proto?.uiMode ?? 0
)
}
return `${classParts[0]}.${classParts[1]}.(...).${className}` function newWindowConfiguration(proto): WindowConfiguration {
return new WindowConfiguration(
toRect(proto.appBounds),
toRect(proto.bounds),
toRect(proto.maxBounds),
proto.windowingMode,
proto.activityType
)
} }
export default WindowContainer export default WindowContainer

View File

@@ -14,43 +14,75 @@
* limitations under the License. * limitations under the License.
*/ */
import { WindowState, Rect } from "../common" import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { toRect, WindowState } from "../common"
import { VISIBLE_CHIP } from '../treeview/Chips' import { VISIBLE_CHIP } from '../treeview/Chips'
import { applyMixins } from '../mixin'
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
export class WindowStateMixin { WindowState.fromProto = function (proto, isActivityInTree: Boolean): WindowState {
visible: boolean if (proto == null) {
return null
get kind() { } else {
return "WindowState" const identifierName = proto.windowContainer.identifier?.title ?? proto.identifier?.title ?? ""
var windowType = 0
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
windowType = WindowState.WINDOW_TYPE_STARTING
} else if (proto.animatingExit) {
windowType = WindowState.WINDOW_TYPE_EXITING
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
windowType = WindowState.WINDOW_TYPE_STARTING
} }
static fromProto(proto) { var nameOverride = identifierName
const windowContainer = WindowContainer.fromProto(proto.windowContainer,
proto.identifier)
const frame = (proto.windowFrames ?? proto).frame ?? {} if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
const rect = new Rect(frame.left ?? 0, frame.top ?? 0, frame.right ?? 0, frame.bottom ?? 0) nameOverride = identifierName.substring(WindowState.STARTING_WINDOW_PREFIX.length)
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
const windowState = nameOverride = identifierName.substring(WindowState.DEBUGGER_WINDOW_PREFIX.length)
new WindowState(windowContainer, /* childWindows */[], rect)
const obj = Object.assign({}, proto)
Object.assign(obj, windowContainer.obj)
delete obj.windowContainer
windowState.attachObject(obj)
return windowState
} }
get chips() { const windowContainer = WindowContainer.fromProto({
return this.visible ? [VISIBLE_CHIP] : [] proto: proto.windowContainer,
nameOverride: nameOverride,
identifierOverride: proto.identifier})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
proto.windowContainer.children.reverse()
.map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
.filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
const entry = new WindowState(
proto.attributes?.type ?? 0,
proto.displayId,
proto.stackId,
proto.animator?.surface?.layer ?? 0,
proto.animator?.surface?.shown ?? false,
windowType,
toRect(proto.windowFrames?.frame ?? null),
toRect(proto.windowFrames?.containingFrame ?? null),
toRect(proto.windowFrames?.parentFrame ?? null),
toRect(proto.windowFrames?.contentFrame ?? null),
toRect(proto.windowFrames?.contentInsets ?? null),
toRect(proto.surfaceInsets),
toRect(proto.givenContentInsets),
toRect(proto.animator?.lastClipRect ?? null),
windowContainer,
/* isAppWindow */ isActivityInTree
)
entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
entry.visible = entry.isVisible ?? false
entry.chips = entry.isVisible ? [VISIBLE_CHIP] : []
entry.children = entry.childrenWindows
if (entry.isVisible) {
entry.rect = entry.rects[0]
}
return entry
} }
} }
applyMixins(WindowState, [WindowStateMixin])
export default WindowState export default WindowState

View File

@@ -14,32 +14,30 @@
* limitations under the License. * limitations under the License.
*/ */
import { getWMPropertiesForDisplay, shortenName } from '../mixin'
import { WindowToken } from "../common" import { WindowToken } from "../common"
import { applyMixins } from '../mixin'
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
export class WindowContainerMixin { WindowToken.fromProto = function (proto, isActivityInTree: Boolean): WindowToken {
get kind() { if (proto == null) {
return "WindowToken" return null
} else {
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer, tokenOverride: proto.hashCode})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
} }
const entry = new WindowToken(windowContainer)
static fromProto(proto) { proto.windowContainer.children.reverse()
const windowContainer = WindowContainer.fromProto(proto.windowContainer, .map(it => WindowContainer.childrenFromProto(entry, it, isActivityInTree))
null) .filter(it => it != null)
.forEach(it => windowContainer.childContainers.push(it))
const windowToken = new WindowToken(windowContainer) entry.obj = getWMPropertiesForDisplay(proto)
entry.shortName = shortenName(entry.name)
const obj = Object.assign({}, proto) entry.children = entry.childrenWindows
Object.assign(obj, windowContainer.obj) return entry
delete obj.windowContainer
windowToken.attachObject(obj)
return windowToken
} }
} }
applyMixins(WindowToken, [WindowContainerMixin])
export default WindowToken export default WindowToken

View File

@@ -373,6 +373,11 @@ function getIntFlagsAsStrings(intFlags, annotationType) {
const mapping = intDefMapping[annotationType].values; const mapping = intDefMapping[annotationType].values;
if (mapping.length == 0) {
console.warn("No mapping for type", annotationType)
return intFlags + ""
}
// Will only contain bits that have not been associated with a flag. // Will only contain bits that have not been associated with a flag.
let leftOver = intFlags; let leftOver = intFlags;