[DO NOT MERGE] Full - Compatibilize winscope with master

Bug: 194813677
Test: none
Change-Id: Ie55429dcb746f597f80dfdaa68cbef19f5820005
This commit is contained in:
Nataniel Borges
2021-07-28 07:13:45 +00:00
parent 642782a098
commit f230a13f30
56 changed files with 2789 additions and 2867 deletions

View File

@@ -115,6 +115,11 @@ TRACE_TARGETS = {
'su root cmd window tracing start\necho "WM trace started."',
'su root cmd window tracing stop >/dev/null 2>&1'
),
"accessibility_trace": TraceTarget(
File("/data/misc/a11ytrace/a11y_trace.pb", "accessibility_trace"),
'su root cmd accessibility start-trace\necho "Accessibility trace started."',
'su root cmd accessibility stop-trace >/dev/null 2>&1'
),
"layers_trace": TraceTarget(
File("/data/misc/wmtrace/layers_trace.pb", "layers_trace"),
'su root service call SurfaceFlinger 1025 i32 1\necho "SF trace started."',

View File

@@ -5,60 +5,60 @@
"author": "Adrian Roos <roosa@google.com>",
"private": true,
"scripts": {
"dev": "cross-env NODE_ENV=development webpack-dev-server --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress --hide-modules",
"dev": "cross-env NODE_ENV=development webpack serve --open --hot",
"build": "cross-env NODE_ENV=production webpack --progress",
"test": "webpack --config webpack.spec.config.js && jasmine dist/bundleSpec.js"
},
"dependencies": {
"cross-env": "^7.0.2",
"jszip": "^3.5.0",
"kotlin": "^1.3.72",
"cross-env": "^7.0.3",
"jszip": "^3.6.0",
"kotlin": "^1.5.21",
"lodash.clonedeep": "^4.5.0",
"ts-loader": "^8.0.3",
"typescript": "^4.0.2",
"vue": "^2.3.3",
"vue-context": "^5.2.0",
"vue-material": "^1.0.0-beta-11",
"vuex": "^3.4.0"
"ts-loader": "^8.3.0",
"typescript": "^4.3.5",
"vue": "^2.6.14",
"vue-context": "^6.0.0",
"vue-material": "^1.0.0-beta-15",
"vuex": "^3.6.2"
},
"devDependencies": {
"@babel/core": "^7.10.5",
"@babel/polyfill": "^7.10.4",
"@babel/preset-env": "^7.10.4",
"@babel/register": "^7.10.5",
"@babel/core": "^7.14.6",
"@babel/polyfill": "^7.12.1",
"@babel/preset-env": "^7.14.7",
"@babel/register": "^7.14.5",
"@jetbrains/kotlin-webpack-plugin": "^3.0.2",
"@testing-library/vue": "^5.1.0",
"@types/lodash": "^4.14.158",
"babel-loader": "^8.1.0",
"compression-webpack-plugin": "^4.0.0",
"cross-env": "^7.0.2",
"css-loader": "^3.6.0",
"eslint": "^7.1.0",
"@testing-library/vue": "^5.8.1",
"@types/lodash": "^4.14.171",
"babel-loader": "^8.2.2",
"compression-webpack-plugin": "^6.1.1",
"cross-env": "^7.0.3",
"css-loader": "^5.2.7",
"eslint": "^7.30.0",
"eslint-config-google": "^0.14.0",
"eslint-plugin-vue": "^6.2.2",
"file-loader": "^6.0.0",
"eslint-plugin-vue": "^7.13.0",
"file-loader": "^6.2.0",
"friendly-errors-webpack-plugin": "^1.7.0",
"html-webpack-inline-source-plugin": "^0.0.10",
"html-webpack-plugin": "3.2.0",
"husky": "^4.2.5",
"jasmine": "^3.5.0",
"lint-staged": ">=10",
"html-webpack-inline-source-plugin": "^1.0.0-beta.2",
"html-webpack-plugin": "4.5.2",
"husky": "^7.0.0",
"jasmine": "^3.8.0",
"lint-staged": "^11.0.1",
"loader-utils": "^2.0.0",
"mini-css-extract-plugin": "^0.9.0",
"mini-css-extract-plugin": "^1.6.2",
"optimize-css-assets-webpack-plugin": "^5.0.3",
"protobufjs": "^6.10.0",
"source-map-loader": "^1.0.1",
"style-loader": "^1.2.1",
"ts-loader": "^8.0.1",
"typescript": "^3.9.7",
"protobufjs": "^6.11.2",
"source-map-loader": "^1.1.3",
"style-loader": "^2.0.0",
"ts-loader": "^8.3.0",
"typescript": "^4.3.5",
"uglifyjs-webpack-plugin": "^2.2.0",
"vue-loader": "^15.9.3",
"vue-style-loader": "^4.1.2",
"vue-template-compiler": "^2.6.11",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.12",
"webpack-dev-server": "^3.11.0",
"webpack-merge": "^5.0.9"
"vue-loader": "^15.9.2",
"vue-style-loader": "^4.1.3",
"vue-template-compiler": "^2.6.14",
"webpack": "^4.46.0",
"webpack-cli": "^4.7.2",
"webpack-dev-server": "^3.11.2",
"webpack-merge": "^5.8.0"
},
"husky": {
"hooks": {

View File

@@ -0,0 +1,35 @@
<!-- Copyright (C) 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.
-->
<template>
<TraceView :store="store" :file="file" :summarizer="summarizer" />
</template>
<script>
import TraceView from "@/TraceView.vue"
export default {
name: "AccessibilityTraceView",
props: ["store", "file"],
components: {
TraceView
},
methods: {
summarizer(item) {
return null;
},
}
}
</script>

View File

@@ -36,7 +36,7 @@
<dataadb class="adbinput" ref="adb" :store="store"
@dataReady="onDataReady" @statusChange="setStatus" />
</div>
<div class="input">
<div class="input" @dragover.prevent @drop.prevent>
<datainput class="fileinput" ref="input" :store="store"
@dataReady="onDataReady" @statusChange="setStatus" />
</div>
@@ -279,4 +279,18 @@ a {
.data-view-container {
padding: 25px 20px 0 20px;
}
.snackbar-break-words {
/* These are technically the same, but use both */
overflow-wrap: break-word;
word-wrap: break-word;
-ms-word-break: break-all;
word-break: break-word;
/* Adds a hyphen where the word breaks, if supported (No Blink) */
-ms-hyphens: auto;
-moz-hyphens: auto;
-webkit-hyphens: auto;
hyphens: auto;
padding: 10px 10px 10px 10px;
}
</style>

View File

@@ -200,6 +200,7 @@ const DUMPS = {
const proxyFileTypeAdapter = {
'window_trace': FILE_TYPES.WINDOW_MANAGER_TRACE,
'accessibility_trace': FILE_TYPES.ACCESSIBILITY_TRACE,
'layers_trace': FILE_TYPES.SURFACE_FLINGER_TRACE,
'wl_trace': FILE_TYPES.WAYLAND_TRACE,
'layers_dump': FILE_TYPES.SURFACE_FLINGER_DUMP,

View File

@@ -13,6 +13,7 @@
limitations under the License.
-->
<template>
<div @dragleave="fileDragOut" @dragover="fileDragIn" @drop="handleFileDrop">
<flat-card style="min-width: 50em">
<md-card-header>
<div class="md-title">Open files</div>
@@ -37,16 +38,6 @@
md-mode="indeterminate"
v-show="loadingFiles"
/>
<div>
<md-checkbox v-model="store.displayDefaults" class="md-primary">
Show default properties
<md-tooltip md-direction="bottom">
If checked, shows the value of all properties.
Otherwise, hides all properties whose value is the default for its
data type.
</md-tooltip>
</md-checkbox>
</div>
<div class="md-layout">
<div class="md-layout-item md-small-size-100">
<md-field>
@@ -100,7 +91,7 @@
:md-active.sync="showSnackbar"
md-persistent
>
<span style="white-space: pre-line;">{{ snackbarText }}</span>
<p class="snackbar-break-words">{{ snackbarText }}</p>
<div @click="hideSnackbarMessage()">
<md-button class="md-icon-button">
<md-icon style="color: white">close</md-icon>
@@ -108,6 +99,7 @@
</div>
</md-snackbar>
</flat-card>
</div>
</template>
<script>
import FlatCard from './components/FlatCard.vue';
@@ -145,7 +137,7 @@ export default {
},
methods: {
showSnackbarMessage(message, duration) {
this.snackbarText = message;
this.snackbarText = '\n' + message + '\n';
this.snackbarDuration = duration;
this.showSnackbar = true;
},
@@ -234,6 +226,18 @@ export default {
});
}
},
fileDragIn(e){
e.preventDefault();
},
fileDragOut(e){
e.preventDefault();
},
handleFileDrop(e) {
e.preventDefault();
let droppedFiles = e.dataTransfer.files;
if(!droppedFiles) return;
this.processFiles(droppedFiles);
},
onLoadFile(e) {
const files = event.target.files || event.dataTransfer.files;
this.processFiles(files);

View File

@@ -30,7 +30,12 @@
<md-icon>save_alt</md-icon>
</md-button>
</md-card-header>
<AccessibilityTraceView
v-if="showInAccessibilityTraceView(file)"
:store="store"
:file="file"
ref="view"
/>
<WindowManagerTraceView
v-if="showInWindowManagerTraceView(file)"
:store="store"
@@ -68,6 +73,7 @@
</template>
<script>
import TraceView from '@/TraceView.vue';
import AccessibilityTraceView from '@/AccessibilityTraceView.vue';
import WindowManagerTraceView from '@/WindowManagerTraceView.vue';
import SurfaceFlingerTraceView from '@/SurfaceFlingerTraceView.vue';
import TransactionsView from '@/TransactionsView.vue';
@@ -152,6 +158,7 @@ export default {
'transactionsview': TransactionsView,
'logview': LogView,
'flat-card': FlatCard,
AccessibilityTraceView,
WindowManagerTraceView,
SurfaceFlingerTraceView,
},

View File

@@ -15,6 +15,12 @@
<template>
<md-card-content class="container">
<div class="navigation">
<md-content
md-tag="md-toolbar"
md-elevation="0"
class="card-toolbar md-transparent md-dense"
>
<h2 class="md-title" style="flex: 1">Log View</h2>
<md-button
class="md-dense md-primary"
@click.native="scrollToRow(lastOccuredVisibleIndex)"
@@ -33,6 +39,7 @@
Pin to latest message
</md-tooltip>
</md-button>
</md-content>
</div>
<div class="filters">

View File

@@ -1,10 +1,6 @@
<template>
<vue-context ref="menu">
<li>
<a href="#" @click.prevent="$emit('collapseAllOtherNodes')">
Collapse all other nodes
</a>
</li>
</vue-context>
</template>

View File

@@ -67,6 +67,15 @@
</div>
<div class="active-timeline" v-show="minimized">
<md-field class="seek-timestamp-field">
<label>Search for timestamp</label>
<md-input v-model="searchTimestamp"></md-input>
</md-field>
<md-button
@click="updateSearchForTimestamp"
>Search</md-button>
<div
class="active-timeline-icon"
@click="$refs.navigationTypeSelection.$el
@@ -81,8 +90,9 @@
</div>
<md-field
v-if="multipleTraces"
ref="navigationTypeSelection"
class="nagivation-style-selection-field"
class="navigation-style-selection-field"
>
<label>Navigation</label>
@@ -91,7 +101,8 @@
name="navigationStyle"
md-dense
>
<md-icon-option :value="NAVIGATION_STYLE.GLOBAL"
<md-icon-option
:value="NAVIGATION_STYLE.GLOBAL"
icon="public"
desc="Consider all timelines for navigation"
/>
@@ -265,7 +276,7 @@ import {NAVIGATION_STYLE} from './utils/consts';
import {TRACE_ICONS} from '@/decode.js';
// eslint-disable-next-line camelcase
import {nanos_to_string} from './transform.js';
import {nanos_to_string, string_to_nanos} from './transform.js';
export default {
name: 'overlay',
@@ -290,6 +301,7 @@ export default {
crop: null,
cropIntent: null,
TRACE_ICONS,
searchTimestamp: '',
};
},
created() {
@@ -376,7 +388,7 @@ export default {
default:
const split = this.navigationStyle.split('-');
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
throw new Error('Unexpected nagivation type');
throw new Error('Unexpected navigation type');
}
const fileType = split[1];
@@ -398,7 +410,7 @@ export default {
default:
const split = this.navigationStyle.split('-');
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
throw new Error('Unexpected nagivation type');
throw new Error('Unexpected navigation type');
}
const fileType = split[1];
@@ -412,8 +424,12 @@ export default {
}
if (this.navigationStyle === NAVIGATION_STYLE.FOCUSED) {
//dumps do not have a timeline, so if scrolling over a dump, show merged timeline
if (this.focusedFile.timeline) {
return this.focusedFile;
}
return this.mergedTimeline;
}
if (this.navigationStyle === NAVIGATION_STYLE.CUSTOM) {
// TODO: Return custom timeline
@@ -425,12 +441,15 @@ export default {
.traces[this.navigationStyle.split('-')[1]];
}
throw new Error('Unexpected Nagivation Style');
throw new Error('Unexpected Navigation Style');
},
isCropped() {
return this.crop != null &&
(this.crop.left !== 0 || this.crop.right !== 1);
},
multipleTraces() {
return this.timelineFiles.length > 1;
},
},
updated() {
this.$nextTick(() => {
@@ -589,7 +608,7 @@ export default {
default:
const split = this.navigationStyle.split('-');
if (split[0] !== NAVIGATION_STYLE.TARGETED) {
throw new Error('Unexpected nagivation type');
throw new Error('Unexpected navigation type');
}
const fileType = split[1];
@@ -623,6 +642,17 @@ export default {
clearSelection() {
this.crop = null;
},
updateSearchForTimestamp() {
if (/^\d+$/.test(this.searchTimestamp)) {
var roundedTimestamp = parseInt(this.searchTimestamp);
} else {
var roundedTimestamp = string_to_nanos(this.searchTimestamp);
}
var closestTimestamp = this.mergedTimeline.timeline.reduce(function(prev, curr) {
return (Math.abs(curr-roundedTimestamp) < Math.abs(prev-roundedTimestamp) ? curr : prev);
});
this.$store.dispatch('updateTimelineTime', parseInt(closestTimestamp));
},
},
components: {
'timeline': Timeline,
@@ -816,7 +846,7 @@ export default {
margin-top: 4px;
}
.nagivation-style-selection-field {
.navigation-style-selection-field {
width: 90px;
margin-right: 10px;
margin-bottom: 0;

View File

@@ -29,7 +29,7 @@
<script>
// eslint-disable-next-line camelcase
import {multiply_rect} from './matrix_utils.js';
import {multiplyRect} from './matrix_utils.js';
export default {
name: 'rects',
@@ -46,9 +46,9 @@ export default {
return this.bounds;
}
const width = Math.max(
...this.rects.map((r) => multiply_rect(r.transform, r).right));
...this.rects.map((r) => multiplyRect(r.transform, r).right));
const height = Math.max(
...this.rects.map((r) => multiply_rect(r.transform, r).bottom));
...this.rects.map((r) => multiplyRect(r.transform, r).bottom));
return {width, height};
},
boundsStyle() {
@@ -57,8 +57,9 @@ export default {
},
filteredRects() {
return this.rects.filter((rect) => {
const isVisible = rect.ref.visible ?? rect.ref.isVisible;
console.warn(`Name: ${rect.ref.name} Kind: ${rect.ref.kind} isVisible=${isVisible}`);
const isVisible = rect.ref.isVisible;
console.warn(`Name: ${rect.ref.name}`, `Kind: ${rect.ref.kind}`,
`isVisible=${isVisible}`);
return isVisible;
});
},
@@ -78,11 +79,23 @@ export default {
const y = this.s(r.top);
const w = this.s(r.right) - this.s(r.left);
const h = this.s(r.bottom) - this.s(r.top);
const t = r.transform;
let t;
if (r.transform && r.transform.matrix) {
t = r.transform.matrix;
} else {
t = r.transform;
}
const tr = t ? `matrix(${t.dsdx}, ${t.dtdx}, ${t.dsdy}, ${t.dtdy}, ` +
`${this.s(t.tx)}, ${this.s(t.ty)})` : '';
return `top: ${y}px; left: ${x}px; height: ${h}px; width: ${w}px;` +
const rectStyle = `top: ${y}px; left: ` +
`${x}px; height: ${h}px; width: ${w}px; ` +
`transform: ${tr}; transform-origin: 0 0;`;
if (r && r.ref) {
console.log(`${r.ref.name} - ${rectStyle}`);
}
return rectStyle;
},
onClick(r) {
this.$emit('rect-click', r.ref);

View File

@@ -30,23 +30,23 @@ export default {
summarizer(layer) {
const summary = [];
if (layer.invisibleDueTo) {
summary.push({key: 'Invisible due to', value: layer.invisibleDueTo});
if (layer?.visibilityReason) {
summary.push({key: 'Invisible due to', value: layer.visibilityReason});
}
if (layer.occludedBy?.length > 0) {
summary.push({key: 'Occluded by', value: layer.occludedBy.join(', ')});
if (layer?.occludedBy?.length > 0) {
summary.push({key: 'Occluded by', value: layer.occludedBy.map(it => it.id).join(', ')});
}
if (layer.partiallyOccludedBy?.length > 0) {
if (layer?.partiallyOccludedBy?.length > 0) {
summary.push({
key: 'Partially occluded by',
value: layer.partiallyOccludedBy.join(', '),
value: layer.partiallyOccludedBy.map(it => it.id).join(', '),
});
}
if (layer.coveredBy?.length > 0) {
summary.push({key: 'Covered by', value: layer.coveredBy.join(', ')});
if (layer?.coveredBy?.length > 0) {
summary.push({key: 'Covered by', value: layer.coveredBy.map(it => it.id).join(', ')});
}
return summary;

View File

@@ -32,7 +32,7 @@
>
<h2 class="md-title" style="flex: 1;">Hierarchy</h2>
<md-checkbox
v-model="showHierachyDiff"
v-model="showHierarchyDiff"
v-if="diffVisualizationAvailable"
>
Show Diff
@@ -72,6 +72,19 @@
class="card-toolbar md-transparent md-dense"
>
<h2 class="md-title" style="flex: 1">Properties</h2>
<div>
<md-checkbox
v-model="displayDefaults"
@change="checkboxChange"
>
Show Defaults
</md-checkbox>
<md-tooltip md-direction="bottom">
If checked, shows the value of all properties.
Otherwise, hides all properties whose value is
the default for its data type.
</md-tooltip>
</div>
<md-checkbox
v-model="showPropertiesDiff"
v-if="diffVisualizationAvailable"
@@ -96,7 +109,6 @@
:item="selectedTree"
:filter="propertyFilter"
:collapseChildren="true"
:useGlobalCollapsedState="true"
:elementView="PropertiesTreeElement"
/>
</div>
@@ -104,7 +116,7 @@
<i class="material-icons none-icon">
filter_none
</i>
<span>No element selected in the hierachy.</span>
<span>No element selected in the hierarchy.</span>
</div>
</div>
</flat-card>
@@ -123,6 +135,8 @@ import {DiffGenerator, defaultModifiedCheck} from './utils/diff.js';
import {TRACE_TYPES, DUMP_TYPES} from './decode.js';
import {stableIdCompatibilityFixup} from './utils/utils.js';
import {CompatibleFeatures} from './utils/compatibility.js';
import {getPropertiesForDisplay} from './flickerlib/mixin';
import ObjectFormatter from './flickerlib/ObjectFormatter';
function formatProto(obj) {
if (obj?.prettyPrint) {
@@ -164,12 +178,16 @@ export default {
item: null,
tree: null,
highlight: null,
showHierachyDiff: false,
showHierarchyDiff: false,
displayDefaults: false,
showPropertiesDiff: false,
PropertiesTreeElement,
};
},
methods: {
checkboxChange(checked) {
this.itemSelected(this.item);
},
itemSelected(item) {
this.hierarchySelected = item;
this.selectedTree = this.getTransformedProperties(item);
@@ -178,8 +196,9 @@ export default {
this.$emit('focus');
},
getTransformedProperties(item) {
ObjectFormatter.displayDefaults = this.displayDefaults;
const transformer = new ObjectTransformer(
item.obj,
getPropertiesForDisplay(item),
item.name,
stableIdCompatibilityFixup(item),
).setOptions({
@@ -189,7 +208,7 @@ export default {
if (this.showPropertiesDiff && this.diffVisualizationAvailable) {
const prevItem = this.getItemFromPrevTree(item);
transformer.withDiff(prevItem?.obj);
transformer.withDiff(getPropertiesForDisplay(prevItem));
}
return transformer.transform();
@@ -200,12 +219,14 @@ export default {
}
},
generateTreeFromItem(item) {
if (!this.showHierachyDiff || !this.diffVisualizationAvailable) {
if (!this.showHierarchyDiff || !this.diffVisualizationAvailable) {
return item;
}
return new DiffGenerator(this.item)
.compareWith(this.getDataWithOffset(-1))
const thisItem = this.item;
const prevItem = this.getDataWithOffset(-1);
return new DiffGenerator(thisItem)
.compareWith(prevItem)
.withUniqueNodeId((node) => {
return node.stableId;
})
@@ -216,7 +237,7 @@ export default {
this.item = item;
this.tree = this.generateTreeFromItem(item);
const rects = item.rects //.toArray()
const rects = item.rects; // .toArray()
this.rects = [...rects].reverse();
this.bounds = item.bounds;
@@ -293,7 +314,7 @@ export default {
selectedIndex() {
this.setData(this.file.data[this.file.selectedIndex ?? 0]);
},
showHierachyDiff() {
showHierarchyDiff() {
this.tree = this.generateTreeFromItem(this.item);
},
showPropertiesDiff() {
@@ -317,7 +338,7 @@ export default {
const hierarchyPropertyFilter =
getFilter(this.hierarchyPropertyFilterString);
return this.store.onlyVisible ? (c) => {
return c.visible && hierarchyPropertyFilter(c);
return c.isVisible && hierarchyPropertyFilter(c);
} : hierarchyPropertyFilter;
},
propertyFilter() {

View File

@@ -33,6 +33,12 @@
v-for="(surface, index) in sufacesAffectedBy(source)"
v-bind:key="surface.id"
>
<span
v-if="simplifyNames && surface.shortName &&
surface.shortName !== surface.name"
>{{surface.shortName}}>
</span>
<span v-else>
<!-- eslint-disable-next-line max-len -->
<span v-if="surface.name" class="surface-name">{{ surface.name }}</span>
<span class="surface-id">
@@ -42,6 +48,7 @@
<!-- eslint-disable-next-line max-len -->
<span v-if="index + 1 < sufacesAffectedBy(source).length">,&nbsp;</span>
</span>
</span>
</div>
<div class="extra-info-column">
<span v-if="source.identifier">
@@ -59,6 +66,8 @@
</template>
<script>
import { shortenName } from './flickerlib/mixin'
export default {
name: 'transaction-entry',
props: {
@@ -83,6 +92,9 @@ export default {
prettifyTransactionId: {
type: Function,
},
simplifyNames: {
type: Boolean,
},
},
computed: {
currentTimestamp() {
@@ -135,8 +147,12 @@ export default {
},
sufacesAffectedBy(transaction) {
if (transaction.type !== 'transaction') {
// TODO (b/162402459): Shorten layer name
return [{name: transaction.layerName, id: transaction.obj.id}];
return [
{
name: transaction.layerName,
shortName: shortenName(transaction.layerName),
id: transaction.obj.id
}];
}
const surfaceIds = new Set();
@@ -145,7 +161,12 @@ export default {
const id = transaction.obj.id;
if (!surfaceIds.has(id)) {
surfaceIds.add(id);
affectedSurfaces.push({name: transaction.layerName, id});
affectedSurfaces.push(
{
name: transaction.layerName,
shortName: shortenName(transaction.layerName),
id
});
}
}

View File

@@ -16,6 +16,13 @@
<md-card-content class="container">
<flat-card class="changes card">
<md-content
md-tag="md-toolbar"
md-elevation="0"
class="card-toolbar md-transparent md-dense"
>
<h2 class="md-title" style="flex: 1">Transactions</h2>
</md-content>
<div class="filters">
<div class="input">
<md-field>
@@ -75,6 +82,11 @@
<div class="md-helper-text">Press enter to add</div>
</md-chips>
</div>
<md-checkbox v-model="trace.simplifyNames">
Simplify names
</md-checkbox>
</div>
<virtual-list style="height: 600px; overflow-y: auto;"
@@ -86,6 +98,7 @@
selectedTransaction,
transactionsTrace,
prettifyTransactionId,
simplifyNames: trace.simplifyNames,
}"
ref="loglist"
/>
@@ -289,7 +302,6 @@ export default {
const perpareForTreeViewTransform = (change) => {
this.removeNullFields(change);
change[META_DATA_KEY] = {
// TODO (b/162402459): Shorten layer name
layerName: change.layerName,
};
// remove redundant properties

View File

@@ -100,7 +100,7 @@
v-on:selected="immediateChildSelected = true"
v-on:unselected="immediateChildSelected = false"
:elementView="elementView"
v-on:collapseSibbling="collapseSibbling"
v-on:collapseSibling="collapseSibling"
v-on:collapseAllOtherNodes="collapseAllOtherNodes"
v-on:closeAllContextMenus="closeAllContextMenus"
ref="children"
@@ -352,9 +352,9 @@ export default {
},
collapseAllOtherNodes() {
this.$emit('collapseAllOtherNodes');
this.$emit('collapseSibbling', this.item);
this.$emit('collapseSibling', this.item);
},
collapseSibbling(item) {
collapseSibling(item) {
if (!this.$refs.children) {
return;
}

View File

@@ -30,8 +30,8 @@ export default {
summarizer(item) {
const summary = [];
if (item.obj.isIncompleteReason) {
summary.push({key: 'Incomplete state reason', value: item.obj.isIncompleteReason});
if (item.isIncompleteReason) {
summary.push({key: 'Incomplete state reason', value: item.isIncompleteReason});
}
return summary;

View File

@@ -0,0 +1,36 @@
{
"invalidProperties": [
"length",
"prototype",
"ref",
"diff",
"rects",
"chips",
"parent",
"timestamp",
"shortName",
"kind",
"resolvedChildren",
"visibilityReason",
"absoluteZ",
"name",
"children",
"stableId"
],
"intDefColumn": {
"WindowLayoutParams.type": "android.view.WindowManager.LayoutParams.WindowType",
"WindowLayoutParams.flags": "android.view.WindowManager.LayoutParams.Flags",
"WindowLayoutParams.privateFlags": "android.view.WindowManager.LayoutParams.PrivateFlags",
"WindowLayoutParams.gravity": "android.view.Gravity.GravityFlags",
"WindowLayoutParams.softInputMode": "android.view.WindowManager.LayoutParams.WindowType",
"WindowLayoutParams.systemUiVisibilityFlags": "android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags",
"WindowLayoutParams.subtreeSystemUiVisibilityFlags": "android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags",
"WindowLayoutParams.behavior": "android.view.WindowInsetsController.Behavior",
"WindowLayoutParams.fitInsetsSides": "android.view.WindowInsets.Side.InsetsSide",
"Configuration.windowingMode": "android.app.WindowConfiguration.WindowingMode",
"WindowConfiguration.windowingMode": "android.app.WindowConfiguration.WindowingMode",
"Configuration.orientation": "android.content.pm.ActivityInfo.ScreenOrientation",
"WindowConfiguration.orientation": "android.content.pm.ActivityInfo.ScreenOrientation",
"WindowState.orientation": "android.content.pm.ActivityInfo.ScreenOrientation"
}
}

View File

@@ -17,6 +17,7 @@
/* eslint-disable camelcase */
/* eslint-disable max-len */
import jsonProtoDefsAccessibility from 'frameworks/base/core/proto/android/server/accessibilitytrace.proto';
import jsonProtoDefsWm from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto';
import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/internal/protolog.proto';
import jsonProtoDefsSf from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto';
@@ -26,16 +27,16 @@ import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/androi
import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto';
import jsonProtoDefsIme from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto';
import protobuf from 'protobufjs';
import {transformLayers, transformLayersTrace} from './transform_sf.js';
import {transform_accessibility_trace} from './transform_accessibility.js';
import {transform_transaction_trace} from './transform_transaction.js';
import {transform_wl_outputstate, transform_wayland_trace} from './transform_wl.js';
import {transformProtolog} from './transform_protolog.js';
import {transform_sysui_trace} from './transform_sys_ui.js';
import {transform_launcher_trace} from './transform_launcher.js';
import {transform_ime_trace_clients, transform_ime_trace_service, transform_ime_trace_managerservice} from './transform_ime.js';
import {fill_transform_data} from './matrix_utils.js';
import {mp4Decoder} from './decodeVideo.js';
import AccessibilityTrace from '@/traces/Accessibility.ts';
import SurfaceFlingerTrace from '@/traces/SurfaceFlinger.ts';
import WindowManagerTrace from '@/traces/WindowManager.ts';
import TransactionsTrace from '@/traces/Transactions.ts';
@@ -52,6 +53,7 @@ import SurfaceFlingerDump from '@/dumps/SurfaceFlinger.ts';
import WindowManagerDump from '@/dumps/WindowManager.ts';
import WaylandDump from '@/dumps/Wayland.ts';
const AccessibilityTraceMessage = lookup_type(jsonProtoDefsAccessibility, 'com.android.server.accessibility.AccessibilityTraceFileProto');
const WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerTraceFileProto');
const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto');
const SfTraceMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersTraceFileProto');
@@ -62,10 +64,11 @@ const WaylandDumpMessage = lookup_type(jsonProtoDefsWl, 'org.chromium.arc.waylan
const ProtoLogMessage = lookup_type(jsonProtoDefsProtoLog, 'com.android.internal.protolog.ProtoLogFileProto');
const SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, 'com.android.systemui.tracing.SystemUiTraceFileProto');
const LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, 'com.android.launcher3.tracing.LauncherTraceFileProto');
const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodClientsTraceFileProto");
const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodServiceTraceFileProto");
const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodManagerServiceTraceFileProto");
const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodClientsTraceFileProto');
const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodServiceTraceFileProto');
const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodManagerServiceTraceFileProto');
const ACCESSIBILITY_MAGIC_NUMBER = [0x09, 0x41, 0x31, 0x31, 0x59, 0x54, 0x52, 0x41, 0x43]; // .A11YTRAC
const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE
const WINDOW_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WINTRACE
const MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42
@@ -73,11 +76,12 @@ const WAYLAND_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x59, 0x4c, 0x54, 0x52, 0x41, 0x
const PROTO_LOG_MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47]; // .PROTOLOG
const SYSTEM_UI_MAGIC_NUMBER = [0x09, 0x53, 0x59, 0x53, 0x55, 0x49, 0x54, 0x52, 0x43]; // .SYSUITRC
const LAUNCHER_MAGIC_NUMBER = [0x09, 0x4C, 0x4E, 0x43, 0x48, 0x52, 0x54, 0x52, 0x43]; // .LNCHRTRC
const IMC_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMCTRACE
const IMS_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMSTRACE
const IMM_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMMTRACE
const IMC_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMCTRACE
const IMS_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMSTRACE
const IMM_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMMTRACE
const FILE_TYPES = Object.freeze({
ACCESSIBILITY_TRACE: 'AccessibilityTrace',
WINDOW_MANAGER_TRACE: 'WindowManagerTrace',
SURFACE_FLINGER_TRACE: 'SurfaceFlingerTrace',
WINDOW_MANAGER_DUMP: 'WindowManagerDump',
@@ -103,8 +107,10 @@ const PROTO_LOG_ICON = 'notes';
const SYSTEM_UI_ICON = 'filter_none';
const LAUNCHER_ICON = 'filter_none';
const IME_ICON = 'keyboard';
const ACCESSIBILITY_ICON = 'filter_none';
const FILE_ICONS = {
[FILE_TYPES.ACCESSIBILITY_TRACE]: ACCESSIBILITY_ICON,
[FILE_TYPES.WINDOW_MANAGER_TRACE]: WINDOW_MANAGER_ICON,
[FILE_TYPES.SURFACE_FLINGER_TRACE]: SURFACE_FLINGER_ICON,
[FILE_TYPES.WINDOW_MANAGER_DUMP]: WINDOW_MANAGER_ICON,
@@ -125,11 +131,8 @@ function oneOf(dataType) {
return {oneOf: true, type: dataType};
}
function manyOf(dataType, fold = null) {
return {manyOf: true, type: dataType, fold};
}
const TRACE_TYPES = Object.freeze({
ACCESSIBILITY: 'AccessibilityTrace',
WINDOW_MANAGER: 'WindowManagerTrace',
SURFACE_FLINGER: 'SurfaceFlingerTrace',
SCREEN_RECORDING: 'ScreenRecording',
@@ -144,6 +147,12 @@ const TRACE_TYPES = Object.freeze({
});
const TRACE_INFO = {
[TRACE_TYPES.ACCESSIBILITY]: {
name: 'Accessibility',
icon: ACCESSIBILITY_ICON,
files: [oneOf(FILE_TYPES.ACCESSIBILITY_TRACE)],
constructor: AccessibilityTrace,
},
[TRACE_TYPES.WINDOW_MANAGER]: {
name: 'WindowManager',
icon: WINDOW_MANAGER_ICON,
@@ -261,6 +270,16 @@ export const TRACE_ICONS = {
// TODO: Rename name to defaultName
const FILE_DECODERS = {
[FILE_TYPES.ACCESSIBILITY_TRACE]: {
name: 'Accessibility trace',
decoder: protoDecoder,
decoderParams: {
type: FILE_TYPES.ACCESSIBILITY_TRACE,
protoType: AccessibilityTraceMessage,
transform: transform_accessibility_trace,
timeline: true,
},
},
[FILE_TYPES.WINDOW_MANAGER_TRACE]: {
name: 'WindowManager trace',
decoder: protoDecoder,
@@ -278,7 +297,7 @@ const FILE_DECODERS = {
type: FILE_TYPES.SURFACE_FLINGER_TRACE,
mime: 'application/octet-stream',
protoType: SfTraceMessage,
transform: transformLayersTrace,
transform: SurfaceFlingerTrace.fromProto,
timeline: true,
},
},
@@ -300,8 +319,8 @@ const FILE_DECODERS = {
type: FILE_TYPES.SURFACE_FLINGER_DUMP,
mime: 'application/octet-stream',
protoType: SfDumpMessage,
transform: (decoded) => transformLayers(true /* includesCompositionState*/, decoded),
timeline: false,
transform: SurfaceFlingerDump.fromProto,
timeline: true,
},
},
[FILE_TYPES.WINDOW_MANAGER_DUMP]: {
@@ -312,7 +331,7 @@ const FILE_DECODERS = {
mime: 'application/octet-stream',
protoType: WmDumpMessage,
transform: WindowManagerDump.fromProto,
timeline: false,
timeline: true,
},
},
[FILE_TYPES.WAYLAND_DUMP]: {
@@ -323,7 +342,7 @@ const FILE_DECODERS = {
mime: 'application/octet-stream',
protoType: WaylandDumpMessage,
transform: transform_wl_outputstate,
timeline: false,
timeline: true,
},
},
[FILE_TYPES.SCREEN_RECORDING]: {
@@ -443,11 +462,6 @@ function modifyProtoFields(protoObj, displayDefaults) {
protoObj[fieldName] = fieldProperties.defaultValue;
}
if (fieldProperties.type === 'TransformProto') {
fill_transform_data(protoObj[fieldName]);
continue;
}
if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
continue;
@@ -524,6 +538,9 @@ function detectAndDecode(buffer, fileName, store) {
if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) {
return decodedFile(FILE_TYPES.SURFACE_FLINGER_TRACE, buffer, fileName, store);
}
if (arrayStartsWith(buffer, ACCESSIBILITY_MAGIC_NUMBER)) {
return decodedFile(FILE_TYPES.ACCESSIBILITY_TRACE, buffer, fileName, store);
}
if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) {
return decodedFile(FILE_TYPES.WINDOW_MANAGER_TRACE, buffer, fileName, store);
}

View File

@@ -16,6 +16,7 @@
import { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
import DumpBase from "./DumpBase";
import LayersTraceEntry from '../flickerlib/layers/LayerTraceEntry'
export default class SurfaceFlinger extends DumpBase {
sfDumpFile: any;
@@ -30,4 +31,12 @@ export default class SurfaceFlinger extends DumpBase {
get type() {
return DUMP_TYPES.SURFACE_FLINGER;
}
static fromProto(proto: any): LayersTraceEntry {
return LayersTraceEntry.fromProto(
/*protos */ proto.layers,
/* timestamp */ 0,
/* hwcBlob */ ""
);
}
}

View File

@@ -32,7 +32,7 @@ export default class WindowManager extends DumpBase {
return DUMP_TYPES.WINDOW_MANAGER;
}
static fromProto(proto): WindowManagerTrace {
static fromProto(proto: any): WindowManagerTrace {
return WindowManagerTrace.fromDump(proto);
}
}

View File

@@ -0,0 +1,35 @@
/*
* 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 { LayersTrace } from "./common"
import LayerTraceEntry from './layers/LayerTraceEntry'
LayersTrace.fromProto = function (proto: any): LayersTrace {
const entries = []
for (const entryProto of proto.entry) {
const transformedEntry = LayerTraceEntry.fromProto(
/* protos */ entryProto.layers.layers,
/* timestamp */ entryProto.elapsedRealtimeNanos,
/* hwcBlob */ entryProto.hwcBlob);
entries.push(transformedEntry);
}
const source = null;
const trace = new LayersTrace(entries, source);
return trace;
}
export default LayersTrace;

View File

@@ -14,48 +14,84 @@
* limitations under the License.
*/
import {toBounds, toBuffer, toColor, toPoint, toRect,
import {toSize, toBuffer, toColor, toPoint, toRect,
toRectF, toRegion, toTransform} from './common';
import intDefMapping from
'../../../../../prebuilts/misc/common/winscope/intDefMapping.json';
import config from '../config/Configuration.json'
function readIntdefMap(): Map<string, string> {
const map = new Map<string, string>();
const keys = Object.keys(config.intDefColumn);
keys.forEach(key => {
const value = config.intDefColumn[key];
map.set(key, value);
});
return map;
}
export default class ObjectFormatter {
private static INVALID_ELEMENT_PROPERTIES = ['length', 'name', 'prototype', 'children',
'childrenWindows', 'ref', 'root', 'layers', 'resolvedChildren']
static displayDefaults: boolean = false
private static INVALID_ELEMENT_PROPERTIES = config.invalidProperties;
private static FLICKER_INTDEF_MAP = new Map([
[`WindowLayoutParams.type`, `android.view.WindowManager.LayoutParams.WindowType`],
[`WindowLayoutParams.flags`, `android.view.WindowManager.LayoutParams.Flags`],
[`WindowLayoutParams.privateFlags`, `android.view.WindowManager.LayoutParams.PrivateFlags`],
[`WindowLayoutParams.gravity`, `android.view.Gravity.GravityFlags`],
[`WindowLayoutParams.softInputMode`, `android.view.WindowManager.LayoutParams.WindowType`],
[`WindowLayoutParams.systemUiVisibilityFlags`, `android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags`],
[`WindowLayoutParams.subtreeSystemUiVisibilityFlags`, `android.view.WindowManager.LayoutParams.SystemUiVisibilityFlags`],
[`WindowLayoutParams.behavior`, `android.view.WindowInsetsController.Behavior`],
[`WindowLayoutParams.fitInsetsSides`, `android.view.WindowInsets.Side.InsetsSide`],
private static FLICKER_INTDEF_MAP = readIntdefMap();
[`Configuration.windowingMode`, `android.app.WindowConfiguration.WindowingMode`],
[`WindowConfiguration.windowingMode`, `android.app.WindowConfiguration.WindowingMode`],
[`Configuration.orientation`, `android.content.pm.ActivityInfo.ScreenOrientation`],
[`WindowConfiguration.orientation`, `android.content.pm.ActivityInfo.ScreenOrientation`],
[`WindowState.orientation`, `android.content.pm.ActivityInfo.ScreenOrientation`],
])
static cloneObject(entry: any): any {
let obj: any = {}
const properties = ObjectFormatter.getProperties(entry);
properties.forEach(prop => obj[prop] = entry[prop]);
return obj;
}
static getProperties(entry: any): string[] {
var props = [];
let obj = entry;
do {
const properties = Object.getOwnPropertyNames(obj).filter(it => {
// filter out functions
if (typeof(entry[it]) === 'function') return false;
// internal propertires from kotlinJs
if (it.includes(`$`)) return false;
// private kotlin variables from kotlin
if (it.startsWith(`_`)) return false;
// some predefined properties used only internally (e.g., children, ref, diff)
if (this.INVALID_ELEMENT_PROPERTIES.includes(it)) return false;
// Flicker object properties or arrays
if (!entry[it]) return false;
const value = entry[it]
// only non-empty arrays of non-flicker objects (otherwise they are in hierarchy)
if (Array.isArray(value) && value.length > 0) return !value[0].stableId
// non-flicker object
return !value.stableId;
});
properties.forEach(function (prop) {
if (typeof(entry[prop]) !== 'function' && props.indexOf(prop) === -1) {
props.push(prop);
}
});
} while (obj = Object.getPrototypeOf(obj));
return props;
}
static format(obj: any): {} {
const entries = Object.entries(obj)
.filter(it => !it[0].includes(`$`))
.filter(it => !this.INVALID_ELEMENT_PROPERTIES.includes(it[0]))
const sortedEntries = entries.sort()
const properties = this.getProperties(obj);
const sortedProperties = properties.sort()
const result: any = {}
sortedEntries.forEach(entry => {
const key = entry[0]
const value: any = entry[1]
sortedProperties.forEach(entry => {
const key = entry;
const value: any = obj[key];
if (value) {
if (value || (this.displayDefaults && value !== undefined && value !== null)) {
// flicker obj
if (value.prettyPrint) {
const isEmpty = value.isEmpty === true;
if (!isEmpty || this.displayDefaults) {
result[key] = value.prettyPrint()
}
} else {
// converted proto to flicker
const translatedObject = this.translateObject(value)
@@ -63,7 +99,11 @@ export default class ObjectFormatter {
result[key] = translatedObject.prettyPrint()
// objects - recursive call
} else if (value && typeof(value) == `object`) {
result[key] = this.format(value)
const childObj = this.format(value) as any
const isEmpty = Object.entries(childObj).length == 0 || childObj.isEmpty
if (!isEmpty || this.displayDefaults) {
result[key] = childObj
}
} else {
// values
result[key] = this.translateIntDef(obj, key, value)
@@ -73,7 +113,8 @@ export default class ObjectFormatter {
}
})
return Object.freeze(result)
// return Object.freeze(result)
return result
}
/**
@@ -86,7 +127,7 @@ export default class ObjectFormatter {
private static translateObject(obj) {
const type = obj?.$type?.name
switch(type) {
case `SizeProto`: return toBounds(obj)
case `SizeProto`: return toSize(obj)
case `ActiveBufferProto`: return toBuffer(obj)
case `ColorProto`: return toColor(obj)
case `PointProto`: return toPoint(obj)

View File

@@ -14,9 +14,6 @@
* limitations under the License.
*/
import { asRawTreeViewObject } from '../utils/diff.js'
import { getPropertiesForDisplay } from './mixin'
import {
KeyguardControllerState,
RootWindowContainer,
@@ -26,17 +23,20 @@ import {
import WindowContainer from "./windows/WindowContainer"
WindowManagerState.fromProto = function ({proto, timestamp = 0, where = ""}): WindowManagerState {
var inputMethodWIndowAppToken = ""
WindowManagerState.fromProto = function (proto: any, timestamp: number = 0, where: string = ""): WindowManagerState {
var inputMethodWIndowAppToken = "";
if (proto.inputMethodWindow != null) {
proto.inputMethodWindow.hashCode.toString(16)
}
const rootWindowContainer = newRootWindowContainer(proto.rootWindowContainer)
const keyguardControllerState = newKeyguardControllerState(
proto.rootWindowContainer.keyguardController)
};
const rootWindowContainer = createRootWindowContainer(proto.rootWindowContainer);
const keyguardControllerState = createKeyguardControllerState(
proto.rootWindowContainer.keyguardController);
const policy = createWindowManagerPolicy(proto.policy);
const entry = new WindowManagerState(
where,
newWindowManagerPolicy(proto.policy),
policy,
proto.focusedApp,
proto.focusedDisplayId,
proto.focusedWindow?.title ?? "",
@@ -47,26 +47,30 @@ WindowManagerState.fromProto = function ({proto, timestamp = 0, where = ""}): Wi
rootWindowContainer,
keyguardControllerState,
timestamp = timestamp
)
entry.kind = entry.constructor.name
entry.rects = entry.windowStates.reverse().map(it => it.rect)
if (!entry.isComplete()) {
entry.isIncompleteReason = entry.getIsIncompleteReason()
}
entry.obj = getPropertiesForDisplay(proto, entry)
entry.shortName = entry.name
entry.chips = []
entry.visible = true
entry.rawTreeViewObject = asRawTreeViewObject(entry)
);
addAttributes(entry, proto);
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
return entry
}
function newWindowManagerPolicy(proto): WindowManagerPolicy {
function addAttributes(entry: WindowManagerState, proto: any) {
entry.kind = entry.constructor.name;
// There no JVM/JS translation for Longs yet
entry.timestampMs = entry.timestamp.toString();
entry.rects = entry.windowStates.reverse().map(it => it.rect);
if (!entry.isComplete()) {
entry.isIncompleteReason = entry.getIsIncompleteReason();
}
entry.proto = proto;
entry.shortName = entry.name;
entry.chips = [];
entry.isVisible = true;
}
function createWindowManagerPolicy(proto: any): WindowManagerPolicy {
return new WindowManagerPolicy(
proto.focusedAppToken || "",
proto.focusedAppToken ?? "",
proto.forceStatusBar,
proto.forceStatusBarFromKeyguard,
proto.keyguardDrawComplete,
@@ -79,36 +83,35 @@ function newWindowManagerPolicy(proto): WindowManagerPolicy {
proto.rotationMode,
proto.screenOnFully,
proto.windowManagerDrawComplete
)
);
}
function newRootWindowContainer(proto): RootWindowContainer {
const children = proto.windowContainer.children.reverse()
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, /* isActivityInTree */ false))
function createRootWindowContainer(proto: any): RootWindowContainer {
const windowContainer = WindowContainer.fromProto(
{proto: proto.windowContainer, children: children})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new RootWindowContainer(windowContainer)
/* proto */ proto.windowContainer,
/* childrenProto */ proto.windowContainer.children.reverse()
);
return entry
if (windowContainer == null) {
throw new Error(`Window container should not be null.\n${JSON.stringify(proto)}`);
}
const entry = new RootWindowContainer(windowContainer);
return entry;
}
function newKeyguardControllerState(proto): KeyguardControllerState {
const keyguardOccludedStates = {}
function createKeyguardControllerState(proto: any): KeyguardControllerState {
const keyguardOccludedStates = {};
if (proto) {
proto.keyguardOccludedStates.forEach(it =>
keyguardOccludedStates[it.displayId] = it.keyguardOccluded)
keyguardOccludedStates[it.displayId] = it.keyguardOccluded);
}
return new KeyguardControllerState(
proto?.isAodShowing ?? false,
proto?.isKeyguardShowing ?? false,
keyguardOccludedStates
)
);
}
export default WindowManagerState;

View File

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

View File

@@ -17,46 +17,58 @@
// Imports all the compiled common Flicker library classes and exports them
// as clean es6 modules rather than having them be commonjs modules
const WindowManagerTrace = require('flicker').com.android.server.wm.traces.common.
windowmanager.WindowManagerTrace;
const WindowManagerState = require('flicker').com.android.server.wm.traces.common.
windowmanager.WindowManagerState;
// WM
const WindowManagerTrace = require('flicker').com.android.server.wm.traces.
common.windowmanager.WindowManagerTrace;
const WindowManagerState = require('flicker').com.android.server.wm.traces.
common.windowmanager.WindowManagerState;
const Activity = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.Activity;
const Configuration = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.Configuration;
const ConfigurationContainer = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.ConfigurationContainer;
const ConfigurationContainer = require('flicker').com.android.server.wm.traces.
common.windowmanager.windows.ConfigurationContainer;
const DisplayArea = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.DisplayArea;
const DisplayContent = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.DisplayContent;
const KeyguardControllerState = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.KeyguardControllerState;
const RootWindowContainer = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.RootWindowContainer;
const KeyguardControllerState = require('flicker').com.android.server.wm.
traces.common.windowmanager.windows.KeyguardControllerState;
const RootWindowContainer = require('flicker').com.android.server.wm.traces.
common.windowmanager.windows.RootWindowContainer;
const Task = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.Task;
const TaskFragment = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.TaskFragment;
const WindowConfiguration = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.WindowConfiguration;
const WindowConfiguration = require('flicker').com.android.server.wm.traces.
common.windowmanager.windows.WindowConfiguration;
const WindowContainer = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.WindowContainer;
const WindowLayoutParams= require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.WindowLayoutParams;
const WindowManagerPolicy = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.WindowManagerPolicy;
const WindowLayoutParams= require('flicker').com.android.server.wm.traces.
common.windowmanager.windows.WindowLayoutParams;
const WindowManagerPolicy = require('flicker').com.android.server.wm.traces.
common.windowmanager.windows.WindowManagerPolicy;
const WindowState = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.WindowState;
const WindowToken = require('flicker').com.android.server.wm.traces.common.
windowmanager.windows.WindowToken;
const Matrix = require('flicker').com.android.server.wm.traces.common.layers.Transform.Matrix;
const Transform = require('flicker').com.android.server.wm.traces.common.layers.Transform;
// SF
const Layer = require('flicker').com.android.server.wm.traces.common.
layers.Layer;
const LayerTraceEntry = require('flicker').com.android.server.wm.traces.common.
layers.LayerTraceEntry;
const LayerTraceEntryBuilder = require('flicker').com.android.server.wm.traces.
common.layers.LayerTraceEntryBuilder;
const LayersTrace = require('flicker').com.android.server.wm.traces.common.
layers.LayersTrace;
const Matrix = require('flicker').com.android.server.wm.traces.common.layers.
Transform.Matrix;
const Transform = require('flicker').com.android.server.wm.traces.common.
layers.Transform;
const Bounds = require('flicker').com.android.server.wm.traces.common.Bounds;
// Common
const Size = require('flicker').com.android.server.wm.traces.common.Size;
const Buffer = require('flicker').com.android.server.wm.traces.common.Buffer;
const Color = require('flicker').com.android.server.wm.traces.common.Color;
const Point = require('flicker').com.android.server.wm.traces.common.Point;
@@ -64,70 +76,129 @@ const Rect = require('flicker').com.android.server.wm.traces.common.Rect;
const RectF = require('flicker').com.android.server.wm.traces.common.RectF;
const Region = require('flicker').com.android.server.wm.traces.common.Region;
function toBounds(proto) {
const EMPTY_BUFFER = new Buffer(0, 0, 0, 0);
const EMPTY_COLOR = new Color(-1, -1, -1, 0);
const EMPTY_RECT = new Rect(0, 0, 0, 0);
const EMPTY_RECTF = new RectF(0, 0, 0, 0);
const EMPTY_POINT = new Point(0, 0);
const EMPTY_MATRIX = new Matrix(0, 0, 0, 0, 0, 0);
const EMPTY_TRANSFORM = new Transform(0, EMPTY_MATRIX);
function toSize(proto) {
if (proto == null) {
return null
return EMPTY_BOUNDS;
}
return new Bounds(proto.width ?? proto.w ?? 0, proto.height ?? proto.h ?? 0)
const width = proto.width ?? proto.w ?? 0;
const height = proto.height ?? proto.h ?? 0;
if (width || height) {
return new Size(width, height);
}
return EMPTY_BOUNDS;
}
function toBuffer(proto) {
if (proto == null) {
return null
const width = proto?.width ?? 0;
const height = proto?.height ?? 0;
const stride = proto?.stride ?? 0;
const format = proto?.format ?? 0;
if (width || height || stride || format) {
return new Buffer(width, height, stride, format);
}
return new Buffer(proto.width ?? 0, proto.height ?? 0, proto.stride ?? 0, proto.format ?? 0)
return EMPTY_BUFFER;
}
function toColor(proto) {
if (proto == null) {
return null
return EMPTY_COLOR;
}
return new Color(proto.r ?? 0, proto.g ?? 0, proto.b ?? 0, proto.a ?? 0)
const r = proto.r ?? 0;
const g = proto.g ?? 0;
const b = proto.b ?? 0;
const a = proto.a ?? 0;
if (r || g || b || a) {
return new Color(r, g, b, a);
}
return EMPTY_COLOR;
}
function toPoint(proto) {
if (proto == null) {
return null
return null;
}
return new Point(proto.x ?? 0, proto.y ?? 0)
const x = proto.x ?? 0;
const y = proto.y ?? 0;
if (x || y) {
return new Point(x, y);
}
return EMPTY_POINT;
}
function toRect(proto) {
if (proto == null) {
return null
return EMPTY_RECT;
}
return new Rect(proto.left ?? 0, proto.top ?? 0, proto.right ?? 0, proto.bottom ?? 0)
const left = proto?.left ?? 0;
const top = proto?.top ?? 0;
const right = proto?.right ?? 0;
const bottom = proto?.bottom ?? 0;
if (left || top || right || bottom) {
return new Rect(left, top, right, bottom);
}
return EMPTY_RECT;
}
function toRectF(proto) {
if (proto == null) {
return null
return EMPTY_RECTF;
}
return new RectF(proto.left ?? 0, proto.top ?? 0, proto.right ?? 0, proto.bottom ?? 0)
const left = proto?.left ?? 0;
const top = proto?.top ?? 0;
const right = proto?.right ?? 0;
const bottom = proto?.bottom ?? 0;
if (left || top || right || bottom) {
return new RectF(left, top, right, bottom);
}
return EMPTY_RECTF;
}
function toRegion(proto) {
if (proto == null) {
return null
return null;
}
let rects = []
for (let rectNr in proto.rect) {
const rect = proto.rect[rectNr]
const parsedRect = toRect(rect)
rects.push(parsedRect)
const rects = [];
for (let x = 0; x < proto.rect.length; x++) {
const rect = proto.rect[x];
const parsedRect = toRect(rect);
rects.push(parsedRect);
}
return new Region(rects)
return new Region(rects);
}
function toTransform(proto) {
if (proto == null) {
return null
return EMPTY_TRANSFORM;
}
const matrix = new Matrix(proto.dsdx ?? 0, proto.dtdx ?? 0,
proto.tx ?? 0, proto.dsdy ?? 0, proto.dtdy ?? 0, proto.ty ?? 0)
return new Transform(proto.type ?? 0, matrix)
const dsdx = proto.dsdx ?? 0;
const dtdx = proto.dtdx ?? 0;
const tx = proto.tx ?? 0;
const dsdy = proto.dsdy ?? 0;
const dtdy = proto.dtdy ?? 0;
const ty = proto.ty ?? 0;
if (dsdx || dtdx || tx || dsdy || dtdy || ty) {
const matrix = new Matrix(dsdx, dtdx, tx, dsdy, dtdy, ty);
return new Transform(proto.type ?? 0, matrix);
}
if (proto.type) {
return new Transform(proto.type ?? 0, EMPTY_MATRIX);
}
return EMPTY_TRANSFORM;
}
export {
@@ -148,19 +219,27 @@ export {
WindowManagerPolicy,
WindowManagerTrace,
WindowManagerState,
Bounds,
// SF
Layer,
LayerTraceEntry,
LayerTraceEntryBuilder,
LayersTrace,
Transform,
Matrix,
// Common
Size,
Buffer,
Color,
Point,
Rect,
RectF,
Region,
toBounds,
toSize,
toBuffer,
toColor,
toPoint,
toRect,
toRectF,
toRegion,
toTransform
toTransform,
};

View File

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

View File

@@ -0,0 +1,95 @@
/*
* Copyright 2021, 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 { Layer, Rect, toBuffer, toColor, toRect, toRectF, toRegion } from "../common"
import { shortenName } from '../mixin'
import { RELATIVE_Z_CHIP, GPU_CHIP, HWC_CHIP } from '../treeview/Chips'
import Transform from './Transform'
Layer.fromProto = function (proto: any): Layer {
const visibleRegion = toRegion(proto.visibleRegion)
const activeBuffer = toBuffer(proto.activeBuffer)
const bounds = toRectF(proto.bounds)
const color = toColor(proto.color)
const screenBounds = toRectF(proto.screenBounds)
const sourceBounds = toRectF(proto.sourceBounds)
const transform = Transform.fromProto(proto.transform, proto.position)
const bufferTransform = Transform.fromProto(proto.bufferTransform, /* position */ null)
const hwcCrop = toRectF(proto.hwcCrop)
const hwcFrame = toRect(proto.hwcFrame)
let crop: Rect
if (proto.crop) {
crop = toRect(proto.crop)
};
const entry = new Layer(
proto.name ?? ``,
proto.id,
proto.parent,
proto.z,
visibleRegion,
activeBuffer,
proto.flags,
bounds,
color,
proto.isOpaque,
proto.shadowRadius,
proto.cornerRadius,
proto.type ?? ``,
screenBounds,
transform,
sourceBounds,
proto.currFrame,
proto.effectiveScalingMode,
bufferTransform,
proto.hwcCompositionType,
hwcCrop,
hwcFrame,
proto.backgroundBlurRadius,
crop,
proto.isRelativeOf,
proto.zOrderRelativeOf
);
addAttributes(entry, proto);
return entry
}
function addAttributes(entry: Layer, proto: any) {
entry.kind = `${entry.id}`;
entry.shortName = shortenName(entry.name);
entry.proto = proto;
entry.rect = entry.bounds;
entry.rect.transform = entry.transform;
entry.rect.ref = entry;
entry.rect.label = entry.name;
entry.chips = [];
updateChips(entry);
}
function updateChips(entry) {
if ((entry.zOrderRelativeOf || -1) !== -1) {
entry.chips.push(RELATIVE_Z_CHIP);
}
if (entry.hwcCompositionType === 'CLIENT') {
entry.chips.push(GPU_CHIP);
} else if (entry.hwcCompositionType === 'DEVICE' || entry.hwcCompositionType === 'SOLID_COLOR') {
entry.chips.push(HWC_CHIP);
}
}
export default Layer;

View File

@@ -0,0 +1,66 @@
/*
* Copyright 2021, 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 { LayerTraceEntry, LayerTraceEntryBuilder } from "../common"
import Layer from './Layer'
import { VISIBLE_CHIP, RELATIVE_Z_PARENT_CHIP, MISSING_LAYER } from '../treeview/Chips'
LayerTraceEntry.fromProto = function (protos: any[], timestamp: number, hwcBlob: string, where: string = ''): LayerTraceEntry {
const layers = protos.map(it => Layer.fromProto(it));
const builder = new LayerTraceEntryBuilder(timestamp, layers, hwcBlob, where);
const entry: LayerTraceEntry = builder.build();
updateChildren(entry);
addAttributes(entry, protos);
return entry;
}
function addAttributes(entry: LayerTraceEntry, protos: any) {
entry.kind = "entry"
// There no JVM/JS translation for Longs yet
entry.timestampMs = entry.timestamp.toString()
entry.rects = entry.visibleLayers
.sort((a, b) => (b.absoluteZ > a.absoluteZ) ? 1 : (a.absoluteZ == b.absoluteZ) ? 0 : -1)
.map(it => it.rect);
// Avoid parsing the entry root because it is an array of layers
// containing all trace information, this slows down the property tree.
// Instead parse only key properties for debugging
const entryIds = {}
protos.forEach(it =>
entryIds[it.id] = `\nparent=${it.parent}\ntype=${it.type}\nname=${it.name}`
);
entry.proto = entryIds;
entry.shortName = entry.name;
entry.chips = [];
entry.isVisible = true;
}
function updateChildren(entry: LayerTraceEntry) {
entry.flattenedLayers.forEach(it => {
if (it.isVisible) {
it.chips.push(VISIBLE_CHIP);
}
if (it.zOrderRelativeOf) {
it.chips.push(RELATIVE_Z_PARENT_CHIP);
}
if (it.isMissing) {
it.chips.push(MISSING_LAYER);
}
});
}
export default LayerTraceEntry;

View File

@@ -0,0 +1,90 @@
/*
* Copyright 2021, 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 { Transform, Matrix } from "../common"
Transform.fromProto = function (transformProto, positionProto): Transform {
const entry = new Transform(
transformProto?.type ?? 0,
getMatrix(transformProto, positionProto))
return entry
}
function getMatrix(transform, position): Matrix {
const x = position?.x ?? 0
const y = position?.y ?? 0
if (transform == null || isSimpleTransform(transform.type)) {
return getDefaultTransform(transform?.type, x, y)
}
return new Matrix(transform.dsdx, transform.dtdx, x, transform.dsdy, transform.dtdy, y)
}
function getDefaultTransform(type, x, y): Matrix {
// IDENTITY
if (!type) {
return new Matrix(1, 0, x, 0, 1, y)
}
// ROT_270 = ROT_90|FLIP_H|FLIP_V
if (isFlagSet(type, ROT_90_VAL | FLIP_V_VAL | FLIP_H_VAL)) {
return new Matrix(0, -1, x, 1, 0, y)
}
// ROT_180 = FLIP_H|FLIP_V
if (isFlagSet(type, FLIP_V_VAL | FLIP_H_VAL)) {
return new Matrix(-1, 0, x, 0, -1, y)
}
// ROT_90
if (isFlagSet(type, ROT_90_VAL)) {
return new Matrix(0, 1, x, -1, 0, y)
}
// IDENTITY
if (isFlagClear(type, SCALE_VAL | ROTATE_VAL)) {
return new Matrix(1, 0, x, 0, 1, y)
}
throw new Error(`Unknown transform type ${type}`)
}
export function isFlagSet(type, bits): Boolean {
var type = type || 0;
return (type & bits) === bits;
}
export function isFlagClear(type, bits): Boolean {
return (type & bits) === 0;
}
export function isSimpleTransform(type): Boolean {
return isFlagClear(type, ROT_INVALID_VAL | SCALE_VAL)
}
/* transform type flags */
const ROTATE_VAL = 0x0002
const SCALE_VAL = 0x0004
/* orientation flags */
const FLIP_H_VAL = 0x0100 // (1 << 0 << 8)
const FLIP_V_VAL = 0x0200 // (1 << 1 << 8)
const ROT_90_VAL = 0x0400 // (1 << 2 << 8)
const ROT_INVALID_VAL = 0x8000 // (0x80 << 8)
export default Transform

View File

@@ -22,12 +22,22 @@ import ObjectFormatter from "./ObjectFormatter"
* @param entry WM hierarchy element
* @param proto Associated proto object
*/
export function getPropertiesForDisplay(proto: any, entry: any): any {
let obj = Object.assign({}, entry)
if (obj.children) delete obj.children
// obj = ObjectFormatter.format(obj)
export function getPropertiesForDisplay(entry: any): any {
if (!entry) {
return
}
obj.proto = Object.assign({}, proto)
let obj: any = {}
const properties = ObjectFormatter.getProperties(entry)
properties.forEach(prop => obj[prop] = entry[prop]);
// 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'
// properties by changing the target element in the hierarchy tree view.
if (obj.children) delete obj.children
if (obj.proto) delete obj.proto
obj.proto = Object.assign({}, entry.proto)
if (obj.proto.children) delete obj.proto.children
if (obj.proto.childWindows) delete obj.proto.childWindows
if (obj.proto.childrenWindows) delete obj.proto.childrenWindows

View File

@@ -18,3 +18,33 @@ import Chip from "./Chip"
import ChipType from "./ChipType"
export const VISIBLE_CHIP = new Chip("V", "visible", ChipType.DEFAULT)
export const RELATIVE_Z_CHIP = {
short: 'RelZ',
long: 'Is relative Z-ordered to another surface',
class: 'warn',
};
export const RELATIVE_Z_PARENT_CHIP = {
short: 'RelZParent',
long: 'Something is relative Z-ordered to this surface',
class: 'warn',
};
export const MISSING_LAYER = {
short: 'MissingLayer',
long: 'This layer was referenced from the parent, but not present in the trace',
class: 'error',
};
export const GPU_CHIP = {
short: 'GPU',
long: 'This layer was composed on the GPU',
class: 'gpu',
};
export const HWC_CHIP = {
short: 'HWC',
long: 'This layer was composed by Hardware Composer',
class: 'hwc',
};

View File

@@ -1,34 +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 Chip from "./Chip"
import { TreeViewObject } from "./types"
export default interface ITreeViewElement {
kind: String
name: String
shortName: String
stableId: Number | String
chips: Chip[]
children: ITreeViewElement[]
// This is used for compatibility with the "legacy" Winscope infrastructure
// where a class object would cause things to not work properly so this should
// return a raw javascript object with the relevant information.
// IMPORTANT: The implementation of this function should always return the
// same object every time it is called and not generate a new object.
asRawTreeViewObject(): TreeViewObject
}

View File

@@ -1,12 +0,0 @@
import Chip from './Chip'
export type TreeViewObject = {
kind: String
name: String
shortName: String
stableId: String | Number
chips: Chip[]
obj: any
children: TreeViewObject[]
ref: any
}

View File

@@ -14,23 +14,22 @@
* limitations under the License.
*/
import { getPropertiesForDisplay, shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { shortenName } from '../mixin'
import { Activity } from "../common"
import WindowContainer from "./WindowContainer"
Activity.fromProto = function (proto): Activity {
Activity.fromProto = function (proto: any): Activity {
if (proto == null) {
return null
return null;
} else {
const children = proto.windowToken.windowContainer.children.reverse()
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, /* isActivityInTree */ true))
const windowContainer = WindowContainer.fromProto({proto: proto.windowToken.windowContainer,
children: children, identifierOverride: proto.identifier})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const windowContainer = WindowContainer.fromProto(
/* proto */ proto.windowToken.windowContainer,
/* protoChildren */ proto.windowToken.windowContainer.children.reverse(),
/* isActivityInTree */ true,
/* nameOverride */ null,
/* identifierOverride */ proto.identifier
);
const entry = new Activity(
proto.name,
proto.state,
@@ -39,16 +38,18 @@ Activity.fromProto = function (proto): Activity {
proto.procId,
proto.translucent,
windowContainer
)
);
entry.obj = getPropertiesForDisplay(proto, entry)
entry.kind = entry.constructor.name
entry.shortName = shortenName(entry.name)
entry.rawTreeViewObject = asRawTreeViewObject(entry)
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
return entry
addAttributes(entry, proto);
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
return entry;
}
}
export default Activity
function addAttributes(entry: Activity, proto: any) {
entry.proto = proto;
entry.kind = entry.constructor.name;
entry.shortName = shortenName(entry.name);
}
export default Activity;

View File

@@ -14,33 +14,33 @@
* limitations under the License.
*/
import { getPropertiesForDisplay, shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { shortenName } from '../mixin'
import { DisplayArea } from "../common"
import WindowContainer from "./WindowContainer"
DisplayArea.fromProto = function (proto, isActivityInTree: Boolean): DisplayArea {
DisplayArea.fromProto = function (proto: any, isActivityInTree: Boolean): DisplayArea {
if (proto == null) {
return null
return null;
} else {
const children = proto.windowContainer.children.reverse()
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer,
children: children, nameOverride: proto.name})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer)
const windowContainer = WindowContainer.fromProto(
/* proto */ proto.windowContainer,
/* protoChildren */ proto.windowContainer.children.reverse(),
/* isActivityInTree */ isActivityInTree,
/* nameOverride */ proto.name
);
entry.obj = getPropertiesForDisplay(proto, entry)
entry.kind = entry.constructor.name
entry.shortName = shortenName(entry.name)
entry.rawTreeViewObject = asRawTreeViewObject(entry)
const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer);
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
return entry
addAttributes(entry, proto);
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
return entry;
}
}
export default DisplayArea
function addAttributes(entry: DisplayArea, proto: any) {
entry.proto = proto;
entry.kind = entry.constructor.name;
entry.shortName = shortenName(entry.name);
}
export default DisplayArea;

View File

@@ -14,31 +14,26 @@
* limitations under the License.
*/
import { getPropertiesForDisplay, shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { shortenName } from '../mixin'
import { toRect, DisplayContent, Rect } from "../common"
import WindowContainer from "./WindowContainer"
DisplayContent.fromProto = function (proto, isActivityInTree: Boolean): DisplayContent {
DisplayContent.fromProto = function (proto: any, isActivityInTree: Boolean): DisplayContent {
if (proto == null) {
return null
return null;
} else {
const children = proto.rootDisplayArea.windowContainer.children.reverse()
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
const windowContainer = WindowContainer.fromProto({proto: proto.rootDisplayArea.windowContainer,
children: children, nameOverride: proto.displayInfo?.name ?? null})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const displayRectWidth = proto.displayInfo?.logicalWidth ?? 0
const displayRectHeight = 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 windowContainer = WindowContainer.fromProto(
/* proto */ proto.rootDisplayArea.windowContainer,
/* protoChildren */ proto.rootDisplayArea.windowContainer.children.reverse(),
/* isActivityInTree */ isActivityInTree,
/* nameOverride */ proto.displayInfo?.name ?? null
);
const displayRectWidth = proto.displayInfo?.logicalWidth ?? 0;
const displayRectHeight = 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,
@@ -59,16 +54,18 @@ DisplayContent.fromProto = function (proto, isActivityInTree: Boolean): DisplayC
proto.displayRotation?.rotation ?? 0,
proto.displayRotation?.lastOrientation ?? 0,
windowContainer
)
);
entry.obj = getPropertiesForDisplay(proto, entry)
entry.kind = entry.constructor.name
entry.shortName = shortenName(entry.name)
entry.rawTreeViewObject = asRawTreeViewObject(entry)
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
return entry
addAttributes(entry, proto);
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
return entry;
}
}
export default DisplayContent
function addAttributes(entry: DisplayContent, proto: any) {
entry.proto = proto;
entry.kind = entry.constructor.name;
entry.shortName = shortenName(entry.name);
}
export default DisplayContent;

View File

@@ -14,24 +14,21 @@
* limitations under the License.
*/
import { getPropertiesForDisplay, shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { shortenName } from '../mixin'
import { Task, toRect } from "../common"
import WindowContainer from "./WindowContainer"
Task.fromProto = function (proto, isActivityInTree: Boolean): Task {
Task.fromProto = function (proto: any, isActivityInTree: Boolean): Task {
if (proto == null) {
return null
return null;
} else {
const windowContainerProto = proto.taskFragment?.windowContainer ?? proto.windowContainer
const children = windowContainerProto.children.reverse()
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
const windowContainer = WindowContainer.fromProto({proto: windowContainerProto,
children: children})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const windowContainerProto = proto.taskFragment?.windowContainer ?? proto.windowContainer;
const windowContainer = WindowContainer.fromProto(
/* proto */ windowContainerProto,
/* protoChildren */ windowContainerProto.children.reverse(),
/* isActivityInTree */ isActivityInTree
);
const entry = new Task(
proto.taskFragment?.activityType ?? proto.activityType,
proto.fillsParent,
@@ -51,16 +48,18 @@ Task.fromProto = function (proto, isActivityInTree: Boolean): Task {
proto.taskFragment?.minWidth ?? proto.minWidth,
proto.taskFragment?.minHeight ?? proto.minHeight,
windowContainer
)
);
entry.obj = getPropertiesForDisplay(proto, entry)
entry.kind = entry.constructor.name
entry.shortName = shortenName(entry.name)
entry.rawTreeViewObject = asRawTreeViewObject(entry)
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
return entry
addAttributes(entry, proto);
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
return entry;
}
}
export default Task
function addAttributes(entry: Task, proto: any) {
entry.proto = proto;
entry.kind = entry.constructor.name;
entry.shortName = shortenName(entry.name);
}
export default Task;

View File

@@ -14,39 +14,36 @@
* limitations under the License.
*/
import { getPropertiesForDisplay, shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { shortenName } from '../mixin'
import { TaskFragment } from "../common"
import WindowContainer from "./WindowContainer"
TaskFragment.fromProto = function (proto, isActivityInTree: Boolean): TaskFragment {
TaskFragment.fromProto = function (proto: any, isActivityInTree: Boolean): TaskFragment {
if (proto == null) {
return null
return null;
} else {
const children = proto.windowContainer.children.reverse()
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer,
children: children})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const windowContainer = WindowContainer.fromProto(
/* proto */ proto.windowContainer,
/* protoChildren */ proto.windowContainer.children.reverse(),
/* isActivityInTree */ isActivityInTree);
const entry = new TaskFragment(
proto.activityType,
proto.displayId,
proto.minWidth,
proto.minHeight,
windowContainer
)
);
entry.obj = getPropertiesForDisplay(proto, entry)
entry.kind = entry.constructor.name
entry.shortName = shortenName(entry.name)
entry.rawTreeViewObject = asRawTreeViewObject(entry)
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
return entry
addAttributes(entry, proto);
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
return entry;
}
}
export default TaskFragment
function addAttributes(entry: TaskFragment, proto: any) {
entry.proto = proto;
entry.kind = entry.constructor.name;
entry.shortName = shortenName(entry.name);
}
export default TaskFragment;

View File

@@ -14,8 +14,7 @@
* limitations under the License.
*/
import { getPropertiesForDisplay, shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { shortenName } from '../mixin'
import {
Configuration,
@@ -34,21 +33,27 @@ import TaskFragment from "./TaskFragment"
import WindowState from "./WindowState"
import WindowToken from "./WindowToken"
WindowContainer.fromProto = function ({
proto,
children,
nameOverride = null,
identifierOverride = null,
tokenOverride = null
}): WindowContainer {
WindowContainer.fromProto = function (
proto: any,
protoChildren: any[],
isActivityInTree: boolean,
nameOverride: string = null,
identifierOverride: string = null,
tokenOverride = null,
): WindowContainer {
if (proto == null) {
return null
return null;
}
const identifier = identifierOverride ?? proto.identifier
var name = nameOverride ?? identifier?.title ?? ""
var token = tokenOverride?.toString(16) ?? identifier?.hashCode?.toString(16) ?? ""
const config = newConfigurationContainer(proto.configurationContainer)
const children = protoChildren
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree));
const identifier = identifierOverride ?? proto.identifier;
var name = nameOverride ?? identifier?.title ?? "";
var token = tokenOverride?.toString(16) ?? identifier?.hashCode?.toString(16) ?? "";
const config = createConfigurationContainer(proto.configurationContainer);
const entry = new WindowContainer(
name,
token,
@@ -56,19 +61,19 @@ WindowContainer.fromProto = function ({
proto.visible,
config,
children
)
);
// 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'
// properties by changing the target element in the hierarchy tree view.
entry.obj = getPropertiesForDisplay(proto, entry)
entry.kind = entry.constructor.name
entry.shortName = shortenName(entry.name)
entry.rawTreeViewObject = asRawTreeViewObject(entry)
return entry
addAttributes(entry, proto);
return entry;
}
WindowContainer.childrenFromProto = function(proto, isActivityInTree: Boolean): WindowContainerChild {
function addAttributes(entry: WindowContainer, proto: any) {
entry.proto = proto;
entry.kind = entry.constructor.name;
entry.shortName = shortenName(entry.name);
}
WindowContainer.childrenFromProto = function(proto: any, isActivityInTree: Boolean): WindowContainerChild {
return DisplayContent.fromProto(proto.displayContent, isActivityInTree) ??
DisplayArea.fromProto(proto.displayArea, isActivityInTree) ??
Task.fromProto(proto.task, isActivityInTree) ??
@@ -76,28 +81,28 @@ WindowContainer.childrenFromProto = function(proto, isActivityInTree: Boolean):
Activity.fromProto(proto.activity) ??
WindowToken.fromProto(proto.windowToken, isActivityInTree) ??
WindowState.fromProto(proto.window, isActivityInTree) ??
WindowContainer.fromProto({proto: proto.windowContainer})
WindowContainer.fromProto(proto.windowContainer);
}
function newConfigurationContainer(proto): ConfigurationContainer {
function createConfigurationContainer(proto: any): ConfigurationContainer {
const entry = new ConfigurationContainer(
newConfiguration(proto?.overrideConfiguration ?? null),
newConfiguration(proto?.fullConfiguration ?? null),
newConfiguration(proto?.mergedOverrideConfiguration ?? null)
)
createConfiguration(proto?.overrideConfiguration ?? null),
createConfiguration(proto?.fullConfiguration ?? null),
createConfiguration(proto?.mergedOverrideConfiguration ?? null)
);
entry.obj = entry
return entry
entry.obj = entry;
return entry;
}
function newConfiguration(proto): Configuration {
function createConfiguration(proto: any): Configuration {
if (proto == null) {
return null
return null;
}
var windowConfiguration = null
var windowConfiguration = null;
if (proto != null && proto.windowConfiguration != null) {
windowConfiguration = newWindowConfiguration(proto.windowConfiguration)
windowConfiguration = createWindowConfiguration(proto.windowConfiguration);
}
return new Configuration(
@@ -109,17 +114,17 @@ function newConfiguration(proto): Configuration {
proto?.smallestScreenWidthDp ?? 0,
proto?.screenLayout ?? 0,
proto?.uiMode ?? 0
)
);
}
function newWindowConfiguration(proto): WindowConfiguration {
function createWindowConfiguration(proto: any): 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,55 +14,35 @@
* limitations under the License.
*/
import { getPropertiesForDisplay, shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { toRect, Bounds, WindowState, WindowLayoutParams } from "../common"
import { shortenName } from '../mixin'
import { toRect, Size, WindowState, WindowLayoutParams } from "../common"
import { VISIBLE_CHIP } from '../treeview/Chips'
import WindowContainer from "./WindowContainer"
WindowState.fromProto = function (proto, isActivityInTree: Boolean): WindowState {
WindowState.fromProto = function (proto: any, isActivityInTree: Boolean): WindowState {
if (proto == null) {
return null
return null;
} else {
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
}
var nameOverride = identifierName
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
nameOverride = identifierName.substring(WindowState.STARTING_WINDOW_PREFIX.length)
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
nameOverride = identifierName.substring(WindowState.DEBUGGER_WINDOW_PREFIX.length)
}
const children = proto.windowContainer.children.reverse()
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
const windowContainer = WindowContainer.fromProto({
proto: proto.windowContainer,
children: children,
nameOverride: nameOverride,
identifierOverride: proto.identifier})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const windowParams = createWindowLayoutParams(proto.attributes);
const identifierName = getIdentifier(proto);
const windowType = getWindowType(proto, identifierName);
const name = getName(identifierName);
const windowContainer = WindowContainer.fromProto(
/* proto */ proto.windowContainer,
/* protoChildren */ proto.windowContainer.children.reverse(),
/* isActivityInTree */ isActivityInTree,
/* nameOverride */ name,
/* identifierOverride */ proto.identifier
);
const entry = new WindowState(
newWindowLayoutParams(proto.attributes),
windowParams,
proto.displayId,
proto.stackId,
proto.animator?.surface?.layer ?? 0,
proto.animator?.surface?.shown ?? false,
windowType,
new Bounds(proto.requestedWidth, proto.requestedHeight),
new Size(proto.requestedWidth, proto.requestedHeight),
toRect(proto.surfacePosition),
toRect(proto.windowFrames?.frame ?? null),
toRect(proto.windowFrames?.containingFrame ?? null),
@@ -74,22 +54,14 @@ import WindowContainer from "./WindowContainer"
toRect(proto.animator?.lastClipRect ?? null),
windowContainer,
/* isAppWindow */ isActivityInTree
)
);
entry.kind = entry.constructor.name
entry.rect = entry.frame
entry.rect.ref = entry
entry.rect.label = entry.name
entry.obj = getPropertiesForDisplay(proto, entry)
entry.shortName = shortenName(entry.name)
entry.visible = entry.isVisible ?? false
entry.chips = entry.isVisible ? [VISIBLE_CHIP] : []
entry.rawTreeViewObject = asRawTreeViewObject(entry)
return entry
addAttributes(entry, proto);
return entry;
}
}
function newWindowLayoutParams(proto): WindowLayoutParams {
function createWindowLayoutParams(proto: any): WindowLayoutParams {
return new WindowLayoutParams(
/* type */ proto?.type ?? 0,
/* x */ proto?.x ?? 0,
@@ -124,4 +96,42 @@ function newWindowLayoutParams(proto): WindowLayoutParams {
)
}
function getWindowType(proto: any, identifierName: string): number {
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
return WindowState.WINDOW_TYPE_STARTING;
} else if (proto.animatingExit) {
return WindowState.WINDOW_TYPE_EXITING;
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
return WindowState.WINDOW_TYPE_STARTING;
}
return 0;
}
function getName(identifierName: string): string {
var name = identifierName;
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) {
name = identifierName.substring(WindowState.STARTING_WINDOW_PREFIX.length);
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) {
name = identifierName.substring(WindowState.DEBUGGER_WINDOW_PREFIX.length);
}
return name;
}
function getIdentifier(proto: any): string {
return proto.windowContainer.identifier?.title ?? proto.identifier?.title ?? "";
}
function addAttributes(entry: WindowState, proto: any) {
entry.kind = entry.constructor.name;
entry.rect = entry.frame;
entry.rect.ref = entry;
entry.rect.label = entry.name;
entry.proto = proto;
entry.shortName = shortenName(entry.name);
entry.chips = entry.isVisible ? [VISIBLE_CHIP] : [];
}
export default WindowState

View File

@@ -14,32 +14,29 @@
* limitations under the License.
*/
import { getPropertiesForDisplay, shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { shortenName } from '../mixin'
import { WindowToken } from "../common"
import WindowContainer from "./WindowContainer"
WindowToken.fromProto = function (proto, isActivityInTree: Boolean): WindowToken {
WindowToken.fromProto = function (proto: any, isActivityInTree: Boolean): WindowToken {
if (proto == null) {
return null
return null;
}
const children = proto.windowContainer.children.reverse()
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree))
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer,
children: children, tokenOverride: proto.hashCode})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new WindowToken(windowContainer)
entry.kind = entry.constructor.name
entry.obj = getPropertiesForDisplay(proto, entry)
entry.shortName = shortenName(entry.name)
entry.rawTreeViewObject = asRawTreeViewObject(entry)
console.warn("Created ", entry.kind, " stableId=", entry.stableId)
return entry
const windowContainer = WindowContainer.fromProto(
/* proto */ proto.windowContainer,
/* protoChildren */ proto.windowContainer.children.reverse(),
/* isActivityInTree */ isActivityInTree,
/* nameOverride */ null,
/* identifierOverride */ null,
/* tokenOverride */ proto.hashCode
);
const entry = new WindowToken(windowContainer);
entry.kind = entry.constructor.name;
entry.proto = proto;
entry.shortName = shortenName(entry.name);
console.warn("Created ", entry.kind, " stableId=", entry.stableId);
return entry;
}
export default WindowToken
export default WindowToken;

View File

@@ -252,6 +252,9 @@ const store = new Vuex.Store({
},
updateTimelineTime(context, timestamp) {
for (const file of context.getters.files) {
//dumps do not have a timeline, so only look at files with timelines to update the timestamp
if (!file.timeline) continue;
const type = file.type;
const entryIndex = findLastMatchingSorted(
file.timeline,

View File

@@ -14,198 +14,38 @@
* limitations under the License.
*/
/* transform type flags */
const TRANSLATE_VAL = 0x0001;
const ROTATE_VAL = 0x0002;
const SCALE_VAL = 0x0004;
/* orientation flags */
const FLIP_H_VAL = 0x0100; // (1 << 0 << 8)
const FLIP_V_VAL = 0x0200; // (1 << 1 << 8)
const ROT_90_VAL = 0x0400; // (1 << 2 << 8)
const ROT_INVALID_VAL = 0x8000; // (0x80 << 8)
function is_proto_2(transform) {
/*
* Checks if the loaded file was a stored with ProtoBuf2 or Protobuf3
*
* Proto2 files don't have a Type for the transform object but all other
* fields of the transform are set.
*
* Proto3 has a type field for the transform but doesn't store default
* values (0 for transform type), also, the framework/native implementation
* doesn't write a transform in case it is an identity matrix.
*/
var propertyNames = Object.getOwnPropertyNames(transform);
return (!propertyNames.includes("type") && propertyNames.includes("dsdx"));
}
function is_simple_transform(transform) {
transform = transform || {};
if (is_proto_2(transform)) {
return false;
}
return is_type_flag_clear(transform, ROT_INVALID_VAL|SCALE_VAL);
}
/**
* Converts a transform type into readable format.
* Adapted from the dump function from framework/native
*
* @param {*} transform Transform object ot be converter
*/
function format_transform_type(transform) {
if (is_proto_2(transform)) {
return "";
}
if (is_type_flag_clear(transform, SCALE_VAL | ROTATE_VAL | TRANSLATE_VAL)) {
return "IDENTITY";
}
var type_flags = [];
if (is_type_flag_set(transform, SCALE_VAL)) {
type_flags.push("SCALE");
}
if (is_type_flag_set(transform, TRANSLATE_VAL)) {
type_flags.push("TRANSLATE");
}
if (is_type_flag_set(transform, ROT_INVALID_VAL)) {
type_flags.push("ROT_INVALID");
} else if (is_type_flag_set(transform, ROT_90_VAL|FLIP_V_VAL|FLIP_H_VAL)) {
type_flags.push("ROT_270");
} else if (is_type_flag_set(transform, FLIP_V_VAL|FLIP_H_VAL)) {
type_flags.push("ROT_180");
} else {
if (is_type_flag_set(transform, ROT_90_VAL)) {
type_flags.push("ROT_90");
}
if (is_type_flag_set(transform, FLIP_V_VAL)) {
type_flags.push("FLIP_V");
}
if (is_type_flag_set(transform, FLIP_H_VAL)) {
type_flags.push("FLIP_H");
}
}
if (type_flags.length == 0) {
throw "Unknown transform type " + transform ;
}
return type_flags.join(', ');
}
/**
* Ensures all values of the transform object are set.
*/
function fill_transform_data(transform) {
function fill_simple_transform(transform) {
// ROT_270 = ROT_90|FLIP_H|FLIP_V;
if (is_type_flag_set(transform, ROT_90_VAL|FLIP_V_VAL|FLIP_H_VAL)) {
transform.dsdx = 0.0;
transform.dtdx = -1.0;
transform.dsdy = 1.0;
transform.dtdy = 0.0;
return;
}
// ROT_180 = FLIP_H|FLIP_V;
if (is_type_flag_set(transform, FLIP_V_VAL|FLIP_H_VAL)) {
transform.dsdx = -1.0;
transform.dtdx = 0.0;
transform.dsdy = 0.0;
transform.dtdy = -1.0;
return;
}
// ROT_90
if (is_type_flag_set(transform, ROT_90_VAL)) {
transform.dsdx = 0.0;
transform.dtdx = 1.0;
transform.dsdy = -1.0;
transform.dtdy = 0.0;
return;
}
// IDENTITY
if (is_type_flag_clear(transform, SCALE_VAL | ROTATE_VAL)) {
transform.dsdx = 1.0;
transform.dtdx = 0.0;
transform.dsdy = 0.0;
transform.dtdy = 1.0;
transform.type = 0;
return;
}
throw "Unknown transform type " + transform;
}
if (!transform) {
return;
}
if (is_proto_2(transform)) {
return;
}
if (is_simple_transform(transform)){
fill_simple_transform(transform);
}
transform.dsdx = transform.dsdx || 0.0;
transform.dtdx = transform.dtdx || 0.0;
transform.dsdy = transform.dsdy || 0.0;
transform.dtdy = transform.dtdy || 0.0;
}
function is_type_flag_set(transform, bits) {
transform = transform || {};
var type = transform.type || 0;
return (type & bits) === bits;
}
function is_type_flag_clear(transform, bits) {
transform = transform || {};
var type = transform.type || 0;
return (type & bits) === 0;
}
function multiply_vec2(matrix, x, y) {
function multiplyVec2(matrix, x, y) {
if (!matrix) return {x, y};
// |dsdx dsdy tx| | x |
// |dtdx dtdy ty| x | y |
// |0 0 1 | | 1 |
return {
x: matrix.dsdx * x + matrix.dsdy * y + matrix.tx,
y: matrix.dtdx * x + matrix.dtdy * y + matrix.ty
y: matrix.dtdx * x + matrix.dtdy * y + matrix.ty,
};
}
function multiply_rect(matrix, rect) {
function multiplyRect(transform, rect) {
let matrix = transform;
if (transform && transform.matrix) {
matrix = transform.matrix;
}
// |dsdx dsdy tx| | left, top |
// matrix = |dtdx dtdy ty| rect = | |
// |0 0 1 | | right, bottom |
var left_top = multiply_vec2(matrix, rect.left, rect.top);
var right_top = multiply_vec2(matrix, rect.right, rect.top);
var left_bottom = multiply_vec2(matrix, rect.left, rect.bottom);
var right_bottom = multiply_vec2(matrix, rect.right, rect.bottom);
const leftTop = multiplyVec2(matrix, rect.left, rect.top);
const rightTop = multiplyVec2(matrix, rect.right, rect.top);
const leftBottom = multiplyVec2(matrix, rect.left, rect.bottom);
const rightBottom = multiplyVec2(matrix, rect.right, rect.bottom);
var outrect = {};
outrect.left = Math.min(left_top.x, right_top.x, left_bottom.x, right_bottom.x);
outrect.top = Math.min(left_top.y, right_top.y, left_bottom.y, right_bottom.y);
outrect.right = Math.max(left_top.x, right_top.x, left_bottom.x, right_bottom.x);
outrect.bottom = Math.max(left_top.y, right_top.y, left_bottom.y, right_bottom.y);
const outrect = {};
outrect.left = Math.min(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x);
outrect.top = Math.min(leftTop.y, rightTop.y, leftBottom.y, rightBottom.y);
outrect.right = Math.max(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x);
outrect.bottom = Math.max(leftTop.y, rightTop.y, leftBottom.y,
rightBottom.y);
return outrect;
}
// Returns true if the applying the transform on an an axis aligned rectangle
// results in another axis aligned rectangle.
function is_simple_rotation(transform) {
return !is_type_flag_set(transform, ROT_INVALID_VAL);
}
export {format_transform_type, fill_transform_data, is_simple_transform,
multiply_rect, is_simple_rotation};
export {multiplyRect};

View File

@@ -19,6 +19,7 @@ import {TRACE_TYPES, DUMP_TYPES} from '@/decode.js';
const mixin = {
showInTraceView(file) {
return file.type == TRACE_TYPES.WINDOW_MANAGER ||
file.type == TRACE_TYPES.ACCESSIBILITY ||
file.type == TRACE_TYPES.SURFACE_FLINGER ||
file.type == TRACE_TYPES.WAYLAND ||
file.type == TRACE_TYPES.SYSTEM_UI ||
@@ -30,6 +31,9 @@ const mixin = {
file.type == DUMP_TYPES.SURFACE_FLINGER ||
file.type == DUMP_TYPES.WAYLAND;
},
showInAccessibilityTraceView(file) {
return file.type == TRACE_TYPES.ACCESSIBILITY;
},
showInWindowManagerTraceView(file) {
return file.type == TRACE_TYPES.WINDOW_MANAGER ||
file.type == DUMP_TYPES.WINDOW_MANAGER;

View File

@@ -229,7 +229,14 @@ export default {
if (this.disabled) {
return;
}
const timestamp = parseInt(this.timeline[clickedOnTsIndex]);
var timestamp = parseInt(this.timeline[clickedOnTsIndex]);
//pointWidth is always 1
//if offset percentage < 1, clickedOnTsIndex becomes negative, leading to a negative index
if (clickedOnTsIndex < 0) {
timestamp = parseInt(this.timeline[0])
}
this.$store.dispatch('updateTimelineTime', timestamp);
},

View File

@@ -1,350 +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.
*/
/**
* Utility class for deriving state and visibility from the hierarchy. This
* duplicates some of the logic in surface flinger. If the trace contains
* composition state (visibleRegion), it will be used otherwise it will be
* derived.
*/
import {multiply_rect, is_simple_rotation} from './matrix_utils.js';
// Layer flags
const FLAG_HIDDEN = 0x01;
const FLAG_OPAQUE = 0x02;
const FLAG_SECURE = 0x80;
function flags_to_string(flags) {
if (!flags) return '';
const verboseFlags = [];
if (flags & FLAG_HIDDEN) verboseFlags.push('HIDDEN');
if (flags & FLAG_OPAQUE) verboseFlags.push('OPAQUE');
if (flags & FLAG_SECURE) verboseFlags.push('SECURE');
return verboseFlags.join('|') + ' (' + flags + ')';
}
function is_empty(region) {
return region == undefined ||
region.rect == undefined ||
region.rect.length == 0 ||
region.rect.every(function(r) {
return is_empty_rect(r);
} );
}
function is_empty_rect(rect) {
const right = rect.right || 0;
const left = rect.left || 0;
const top = rect.top || 0;
const bottom = rect.bottom || 0;
return (right - left) <= 0 || (bottom - top) <= 0;
}
function is_rect_empty_and_valid(rect) {
return rect &&
(rect.left - rect.right === 0 || rect.top - rect.bottom === 0);
}
/**
* The transformation matrix is defined as the product of:
* | cos(a) -sin(a) | \/ | X 0 |
* | sin(a) cos(a) | /\ | 0 Y |
*
* where a is a rotation angle, and X and Y are scaling factors.
* A transformation matrix is invalid when either X or Y is zero,
* as a rotation matrix is valid for any angle. When either X or Y
* is 0, then the scaling matrix is not invertible, which makes the
* transformation matrix not invertible as well. A 2D matrix with
* components | A B | is not invertible if and only if AD - BC = 0.
* | C D |
* This check is included above.
*/
function is_transform_invalid(transform) {
return !transform || (transform.dsdx * transform.dtdy ===
transform.dtdx * transform.dsdy); // determinant of transform
}
function is_opaque(layer) {
if (layer.color == undefined || layer.color.a == undefined || layer.color.a != 1) return false;
return layer.isOpaque;
}
function fills_color(layer) {
return layer.color && layer.color.a > 0 &&
layer.color.r >= 0 && layer.color.g >= 0 &&
layer.color.b >= 0;
}
function draws_shadows(layer) {
return layer.shadowRadius && layer.shadowRadius > 0;
}
function has_blur(layer) {
return layer.backgroundBlurRadius && layer.backgroundBlurRadius > 0;
}
function has_effects(layer) {
// Support previous color layer
if (layer.type === 'ColorLayer') return true;
// Support newer effect layer
return layer.type === 'EffectLayer' &&
(fills_color(layer) || draws_shadows(layer) || has_blur(layer));
}
function is_hidden_by_policy(layer) {
return layer.flags & FLAG_HIDDEN == FLAG_HIDDEN ||
// offscreen layer root has a unique layer id
layer.id == 0x7FFFFFFD;
}
/**
* Checks if the layer is visible based on its visibleRegion if available
* or its type, active buffer content, alpha and properties.
*/
function is_visible(layer, hiddenByPolicy, includesCompositionState) {
if (includesCompositionState) {
return !is_empty(layer.visibleRegion);
}
if (hiddenByPolicy) {
return false;
}
if (!layer.activeBuffer && !has_effects(layer)) {
return false;
}
if (!layer.color || !layer.color.a || layer.color.a == 0) {
return false;
}
if (layer.occludedBy && layer.occludedBy.length > 0) {
return false;
}
if (!layer.bounds || is_empty_rect(layer.bounds)) {
return false;
}
return true;
}
function get_visibility_reason(layer, includesCompositionState) {
if (layer.type === 'ContainerLayer') {
return 'ContainerLayer';
}
if (is_hidden_by_policy(layer)) {
return 'Flag is hidden';
}
if (layer.hidden) {
return 'Hidden by parent';
}
const isBufferLayer = (layer.type === 'BufferStateLayer' ||
layer.type === 'BufferQueueLayer');
if (isBufferLayer && (!layer.activeBuffer ||
layer.activeBuffer.height === 0 || layer.activeBuffer.width === 0)) {
return 'Buffer is empty';
}
if (!layer.color || !layer.color.a || layer.color.a == 0) {
return 'Alpha is 0';
}
if (is_rect_empty_and_valid(layer.crop)) {
return 'Crop is 0x0';
}
if (!layer.bounds || is_empty_rect(layer.bounds)) {
return 'Bounds is 0x0';
}
if (is_transform_invalid(layer.transform)) {
return 'Transform is invalid';
}
if (layer.isRelativeOf && layer.zOrderRelativeOf == -1) {
return 'RelativeOf layer has been removed';
}
const isEffectLayer = (layer.type === 'EffectLayer');
if (isEffectLayer && !fills_color(layer) &&
!draws_shadows(layer) && !has_blur(layer)) {
return 'Effect layer does not have color fill, shadow or blur';
}
if (layer.occludedBy && layer.occludedBy.length > 0) {
return 'Layer is occluded by:' + layer.occludedBy.join();
}
if (includesCompositionState && is_empty(layer.visibleRegion)) {
return 'Visible region calculated by Composition Engine is empty';
}
if (layer.visible) {
return 'Unknown';
};
}
// Returns true if rectA overlaps rectB
function overlaps(rectA, rectB) {
return rectA.left < rectB.right && rectA.right > rectB.left &&
rectA.top < rectB.bottom && rectA.bottom > rectA.top;
}
// Returns true if outer rect contains inner rect
function contains(outerLayer, innerLayer) {
if (!is_simple_rotation(outerLayer.transform) ||
!is_simple_rotation(innerLayer.transform)) {
return false;
}
const outer = screen_bounds(outerLayer);
const inner = screen_bounds(innerLayer);
return inner.left >= outer.left && inner.top >= outer.top &&
inner.right <= outer.right && inner.bottom <= outer.bottom;
}
function screen_bounds(layer) {
if (layer.screenBounds) return layer.screenBounds;
const transformMatrix = layer.transform;
const tx = layer.position ? layer.position.x || 0 : 0;
const ty = layer.position ? layer.position.y || 0 : 0;
transformMatrix.tx = tx;
transformMatrix.ty = ty;
return multiply_rect(transformMatrix, layer.bounds);
}
// Traverse in z-order from top to bottom and fill in occlusion data
function fill_occlusion_state(layerMap, rootLayers, includesCompositionState) {
const layers = rootLayers.filter((layer) => !layer.isRelativeOf);
traverse_top_to_bottom(layerMap, layers, {opaqueRects: [], transparentRects: [], screenBounds: null}, (layer, globalState) => {
if (layer.name.startsWith('Root#0') && layer.sourceBounds) {
globalState.screenBounds = {left: 0, top: 0, bottom: layer.sourceBounds.bottom, right: layer.sourceBounds.right};
}
const visible = is_visible(layer, layer.hidden, includesCompositionState);
if (visible) {
const fullyOccludes = (testLayer) => contains(testLayer, layer) && !layer.cornerRadius;
const partiallyOccludes = (testLayer) => overlaps(testLayer, layer);
const covers = (testLayer) => overlaps(testLayer, layer);
layer.occludedBy = globalState.opaqueRects.filter(fullyOccludes).map((layer) => layer.id);
layer.partiallyOccludedBy = globalState.opaqueRects.filter(partiallyOccludes)
.filter((p) => layer.occludedBy.indexOf(p.id) == -1)
.map((layer) => layer.id);
layer.coveredBy = globalState.transparentRects.filter(covers).map((layer) => layer.id);
if (is_opaque(layer)) {
globalState.opaqueRects.push(layer);
} else {
globalState.transparentRects.push(layer);
}
}
layer.visible = is_visible(layer, layer.hidden, includesCompositionState);
if (!layer.visible) {
layer.invisibleDueTo = get_visibility_reason(layer, includesCompositionState);
}
});
}
function traverse_top_to_bottom(layerMap, rootLayers, globalState, fn) {
for (let i = rootLayers.length-1; i >=0; i--) {
const relatives = [];
for (const id of rootLayers[i].relatives) {
if (!layerMap.hasOwnProperty(id)) {
// TODO (b/162500053): so that this doesn't need to be checked here
console.warn(
`Relative layer with id ${id} not found in dumped layers... ` +
`Skipping layer in traversal...`);
} else {
relatives.push(layerMap[id]);
}
}
const children = [];
for (const id of rootLayers[i].children) {
if (!layerMap.hasOwnProperty(id)) {
// TODO (b/162500053): so that this doesn't need to be checked here
console.warn(
`Child layer with id ${id} not found in dumped layers... ` +
`Skipping layer in traversal...`);
} else {
children.push(layerMap[id]);
}
}
// traverse through relatives and children that are not relatives
const traverseList = relatives
.concat(children.filter((layer) => !layer.isRelativeOf));
traverseList.sort((lhs, rhs) => rhs.z - lhs.z);
traverseList.filter((layer) => layer.z >=0).forEach((layer) => {
traverse_top_to_bottom(layerMap, [layer], globalState, fn);
});
fn(rootLayers[i], globalState);
traverseList.filter((layer) => layer.z < 0).forEach((layer) => {
traverse_top_to_bottom(layerMap, [layer], globalState, fn);
});
}
}
// Traverse all children and fill in any inherited states.
function fill_inherited_state(layerMap, rootLayers) {
traverse(layerMap, rootLayers, (layer, parent) => {
const parentHidden = parent && parent.hidden;
layer.hidden = is_hidden_by_policy(layer) || parentHidden;
layer.verboseFlags = flags_to_string(layer.flags);
if (!layer.bounds) {
if (!layer.sourceBounds) {
layer.bounds = layer.sourceBounds;
} else if (parent) {
layer.bounds = parent.bounds;
} else {
layer.bounds = {left: 0, top: 0, right: 0, bottom: 0};
}
}
});
}
function traverse(layerMap, rootLayers, fn) {
for (let i = rootLayers.length-1; i >=0; i--) {
const parentId = rootLayers[i].parent;
const parent = parentId == -1 ? null : layerMap[parentId];
fn(rootLayers[i], parent);
const children = rootLayers[i].children.map(
(id) => {
const child = layerMap[id];
if (child == null) {
console.warn(
`Child layer with id ${id} in parent layer id ${rootLayers[i].id} not found... ` +
`Skipping layer in traversal...`);
}
return child;
}).filter(item => item !== undefined);
traverse(layerMap, children, fn);
}
}
export {fill_occlusion_state, fill_inherited_state};

View File

@@ -14,8 +14,20 @@
* limitations under the License.
*/
import ITreeViewElement from "./ITreeViewElement"
import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
import TraceBase from './TraceBase';
export default interface IClickableTreeViewElement extends ITreeViewElement {
obj: any
export default class Accessibility extends TraceBase {
accessibilityTraceFile: Object;
constructor(files) {
const accessibilityTraceFile = files[FILE_TYPES.ACCESSIBILITY_TRACE];
super(accessibilityTraceFile.data, accessibilityTraceFile.timeline, files);
this.accessibilityTraceFile = accessibilityTraceFile;
}
get type() {
return TRACE_TYPES.ACCESSIBILITY;
}
}

View File

@@ -16,9 +16,10 @@
import { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
import TraceBase from './TraceBase';
import { LayersTrace } from '@/flickerlib';
export default class SurfaceFlinger extends TraceBase {
sfTraceFile: any;
sfTraceFile: Object;
constructor(files) {
const sfTraceFile = files[FILE_TYPES.SURFACE_FLINGER_TRACE];
@@ -30,4 +31,8 @@ export default class SurfaceFlinger extends TraceBase {
get type() {
return TRACE_TYPES.SURFACE_FLINGER;
}
static fromProto(proto: any): LayersTrace {
return LayersTrace.fromProto(proto);
}
}

View File

@@ -33,7 +33,7 @@ export default class WindowManager extends TraceBase {
return TRACE_TYPES.WINDOW_MANAGER;
}
static fromProto(proto): WindowManagerTrace {
static fromProto(proto: any): WindowManagerTrace {
return WindowManagerTrace.fromProto(proto);
}
}

View File

@@ -103,7 +103,7 @@ function transform({
stableId: stableIdResolved,
visible: call(visible, obj),
childrenVisible: transformedChildren.some((c) => {
return c.childrenVisible || c.visible;
return c.childrenVisible || c.isVisible;
}),
flattened: call(flattened, obj),
};
@@ -309,7 +309,7 @@ class ObjectTransformer {
transformedObj = {
kind: '',
name: name + ': ' + child.name,
name: (isTerminal(name) ? compareWithName : name) + ': ' + child.name,
stableId,
children: child.children,
combined: true,
@@ -376,6 +376,24 @@ function nanos_to_string(elapsedRealtimeNanos) {
return parts.reverse().join('');
}
function string_to_nanos(stringTime) {
//isolate the times for each unit in an array
var times = stringTime.split(/\D+/).filter(unit => unit.length > 0);
//add zeroes to start of array if only partial timestamp is input
while (times.length<5) {
times.unshift("0");
}
var units = [24*60*60, 60*60, 60, 1, 0.001];
var nanos = 0;
//multiply the times by the relevant unit and sum
for (var x=0; x<5; x++) {
nanos += units[x]*parseInt(times[x]);
}
return nanos*(10**9);
}
// Returns a UI element used highlight a visible entry.
// eslint-disable-next-line camelcase
function get_visible_chip() {
@@ -383,4 +401,4 @@ function get_visible_chip() {
}
// eslint-disable-next-line camelcase
export {transform, ObjectTransformer, nanos_to_string, get_visible_chip};
export {transform, ObjectTransformer, nanos_to_string, string_to_nanos, get_visible_chip};

View File

@@ -0,0 +1,52 @@
/*
* 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 { transform, nanos_to_string, get_visible_chip } from './transform.js'
function transform_accessibility(accessibility) {
return transform({
obj: accessibility,
kind: 'accessibility',
name: 'accessibility',
children: []
});
}
function transform_entry(entry) {
return transform({
obj: entry,
kind: 'entry',
name: nanos_to_string(entry.elapsedRealtimeNanos),
children: [
[entry.accessibilityService, transform_accessibility],
],
timestamp: entry.elapsedRealtimeNanos,
stableId: 'entry'
});
}
function transform_accessibility_trace(entries) {
return transform({
obj: entries,
kind: 'entries',
name: 'entries',
children: [
[entries.entry, transform_entry],
],
});
}
export { transform_accessibility_trace };

View File

@@ -1,240 +0,0 @@
/*
* Copyright 2017, 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.
*/
// eslint-disable-next-line camelcase
import {transform, nanos_to_string, get_visible_chip} from './transform.js';
// eslint-disable-next-line camelcase
import {fill_occlusion_state, fill_inherited_state} from './sf_visibility.js';
import {getSimplifiedLayerName} from './utils/names';
import ObjectFormatter from './flickerlib/ObjectFormatter'
const RELATIVE_Z_CHIP = {
short: 'RelZ',
long: 'Is relative Z-ordered to another surface',
class: 'warn',
};
const RELATIVE_Z_PARENT_CHIP = {
short: 'RelZParent',
long: 'Something is relative Z-ordered to this surface',
class: 'warn',
};
const MISSING_LAYER = {
short: 'MissingLayer',
long:
'This layer was referenced from the parent, but not present in the trace',
class: 'error',
};
const GPU_CHIP = {
short: 'GPU',
long: 'This layer was composed on the GPU',
class: 'gpu',
};
const HWC_CHIP = {
short: 'HWC',
long: 'This layer was composed by Hardware Composer',
class: 'hwc',
};
function transformLayer(layer) {
function offsetTo(bounds, x, y) {
return {
right: bounds.right - (bounds.left - x),
bottom: bounds.bottom - (bounds.top - y),
left: x,
top: y,
};
}
function getRect(layer) {
let result = layer.bounds;
const tx = layer.position ? layer.position.x || 0 : 0;
const ty = layer.position ? layer.position.y || 0 : 0;
result.label = layer.name;
result.transform = layer.transform;
result.transform.tx = tx;
result.transform.ty = ty;
return result;
}
function addHwcCompositionTypeChip(layer) {
if (layer.hwcCompositionType === 'CLIENT') {
chips.push(GPU_CHIP);
} else if (layer.hwcCompositionType === 'DEVICE' ||
layer.hwcCompositionType === 'SOLID_COLOR') {
chips.push(HWC_CHIP);
}
}
const chips = [];
if (layer.visible) {
chips.push(get_visible_chip());
}
if ((layer.zOrderRelativeOf || -1) !== -1) {
chips.push(RELATIVE_Z_CHIP);
}
if (layer.zOrderRelativeParentOf !== undefined) {
chips.push(RELATIVE_Z_PARENT_CHIP);
}
if (layer.missing) {
chips.push(MISSING_LAYER);
}
addHwcCompositionTypeChip(layer);
const rect = layer.visible && layer.bounds !== null ?
getRect(layer) : undefined;
const simplifiedLayerName = getSimplifiedLayerName(layer.name);
const shortName = simplifiedLayerName ?
layer.id + ': ' + simplifiedLayerName : undefined;
const transformedLayer = transform({
obj: ObjectFormatter.format(layer),
kind: '',
name: layer.id + ': ' + layer.name,
shortName,
children: [[layer.resolvedChildren, transformLayer]],
rect,
undefined /* bounds */,
highlight: rect,
chips,
visible: layer.visible,
freeze: false,
});
// NOTE: Temporary until refactored to use flickerlib
transformedLayer.invisibleDueTo = layer.invisibleDueTo;
transformedLayer.occludedBy = layer.occludedBy;
transformedLayer.partiallyOccludedBy = layer.partiallyOccludedBy;
transformedLayer.coveredBy = layer.coveredBy;
return Object.freeze(transformedLayer);
}
function missingLayer(childId) {
return {
name: 'layer #' + childId,
missing: true,
zOrderRelativeOf: -1,
transform: {dsdx: 1, dtdx: 0, dsdy: 0, dtdy: 1},
};
}
function transformLayers(includesCompositionState, layers) {
const idToItem = {};
const isChild = {};
const layersList = layers.layers || [];
layersList.forEach((e) => {
idToItem[e.id] = e;
});
layersList.forEach((e) => {
e.resolvedChildren = [];
if (Array.isArray(e.children)) {
e.resolvedChildren = e.children.map(
(childId) => idToItem[childId] || missingLayer(childId));
e.children.forEach((childId) => {
isChild[childId] = true;
});
}
// We don't clean up relatives when the relative parent is removed, so it
// may be inconsistent
if ((e.zOrderRelativeOf || -1) !== -1 && (idToItem[e.zOrderRelativeOf])) {
idToItem[e.zOrderRelativeOf].zOrderRelativeParentOf = e.id;
}
});
const roots = layersList.filter((e) => !isChild[e.id]);
fill_inherited_state(idToItem, roots);
// Backwards compatibility check
const occlusionDetectionCompatible = roots[0].bounds !== null;
if (occlusionDetectionCompatible) {
fill_occlusion_state(idToItem, roots, includesCompositionState);
}
function foreachTree(nodes, fun) {
nodes.forEach((n) => {
fun(n);
foreachTree(n.children, fun);
});
}
const idToTransformed = {};
const transformedRoots = roots.map((r) =>
transformLayer(r, {
parentBounds: {left: 0, right: 0, top: 0, bottom: 0},
parentHidden: null,
}));
foreachTree(transformedRoots, (n) => {
idToTransformed[n.obj.id] = n;
});
const flattened = [];
layersList.forEach((e) => {
flattened.push(idToTransformed[e.id]);
});
return transform({
obj: {},
kind: 'layers',
name: 'layers',
children: [
[transformedRoots, (c) => c],
],
rectsTransform(r) {
const res = [];
flattened.forEach((l) => {
if (l.rect) {
res.push(l.rect);
}
});
return res.reverse();
},
flattened,
});
}
function transformLayersEntry(entry) {
const includesCompositionState = !entry.excludesCompositionState;
return transform({
obj: entry,
kind: 'entry',
name: nanos_to_string(entry.elapsedRealtimeNanos) + ' - ' + entry.where,
children: [
[
[entry.layers],
(layer) => transformLayers(includesCompositionState, layer),
],
],
timestamp: entry.elapsedRealtimeNanos,
stableId: 'entry',
});
}
function transformLayersTrace(entries) {
const r = transform({
obj: entries,
kind: 'layerstrace',
name: 'layerstrace',
children: [
[entries.entry, transformLayersEntry],
],
});
return r;
}
export {transformLayers, transformLayersTrace};

View File

@@ -15,7 +15,7 @@
*/
// TODO (b/162300507): Get rid of cloning
import cloneDeep from 'lodash.clonedeep';
import ObjectFormatter from '../flickerlib/ObjectFormatter';
export const DiffType = Object.freeze({
NONE: 'none',
@@ -26,21 +26,6 @@ export const DiffType = Object.freeze({
MODIFIED: 'modified',
});
export function asRawTreeViewObject(obj) {
const children = obj.children?.map(child => child.rawTreeViewObject) ?? []
return {
kind: obj.kind,
name: obj.name,
shortName: obj.shortName,
stableId: obj.stableId,
chips: obj.chips,
obj: obj.obj,
children,
ref: obj,
};
}
export function defaultModifiedCheck(newNode, oldNode) {
if (!newNode && !oldNode) {
return false;
@@ -50,29 +35,16 @@ export function defaultModifiedCheck(newNode, oldNode) {
return true;
}
return JSON.stringify(newNode.obj) !== JSON.stringify(oldNode.obj);
}
function isPrimitive(test) {
return test !== Object(test);
return !newNode.equals(oldNode);
}
export class DiffGenerator {
constructor(tree) {
if (tree.rawTreeViewObject) {
this.tree = tree.rawTreeViewObject;
} else {
this.tree = tree;
}
}
compareWith(tree) {
if (tree?.rawTreeViewObject) {
this.diffWithTree = tree.rawTreeViewObject;
} else {
this.diffWithTree = tree;
}
return this;
}
@@ -140,16 +112,13 @@ export class DiffGenerator {
return Object.keys(obj).length === 0 && obj.constructor === Object;
}
_cloneNodeWithoutChildren(node) {
const clone = {};
for (const key in node) {
if (key !== 'children') {
const val = node[key];
clone[key] = isPrimitive(val) ? val : cloneDeep(val);
}
}
_cloneNode(node) {
const clone = ObjectFormatter.cloneObject(node);
clone.children = node.children;
clone.name = node.name;
clone.kind = node.kind;
clone.stableId = node.stableId;
clone.shortName = node.shortName;
return clone;
}
@@ -167,7 +136,7 @@ export class DiffGenerator {
// Deep clone newTree omitting children field
// Clone is required because trees are frozen objects — we can't
// modify the original tree object. Also means there is no side effect.
const diffTree = this._cloneNodeWithoutChildren(newTree);
const diffTree = this._cloneNode(newTree);
// Default to no changes
diffTree.diff = {type: DiffType.NONE};
@@ -200,7 +169,7 @@ export class DiffGenerator {
// Check if oldTree has been deleted of moved
if (oldTree && !newTreeSiblingIds.includes(oldId)) {
const deletedTreeDiff = this._cloneNodeWithoutChildren(oldTree);
const deletedTreeDiff = this._cloneNode(oldTree);
if (this.newMapping[oldId]) {
deletedTreeDiff.diff = {type: DiffType.DELETED_MOVE};
@@ -233,7 +202,7 @@ export class DiffGenerator {
} else if (oldTree) {
if (!newTreeSiblingIds.includes(oldId)) {
// Deep clone oldTree omitting children field
const diffTree = this._cloneNodeWithoutChildren(oldTree);
const diffTree = this._cloneNode(oldTree);
// newTree doesn't exists, oldTree has either been moved or deleted.
if (this.newMapping[oldId]) {
@@ -258,12 +227,12 @@ export class DiffGenerator {
// TODO: Try replacing this with some sort of zipWith.
const numOfChildren = Math.max(
newTree?.children.length ?? 0, oldTree?.children.length ?? 0);
newTree?.children?.length ?? 0, oldTree?.children?.length ?? 0);
for (let i = 0; i < numOfChildren; i++) {
const newChild = i < newTree?.children.length ?
const newChild = i < newTree?.children?.length ?
newTree.children[i] : null;
const oldChild = i < oldTree?.children.length ?
const oldChild = i < oldTree?.children?.length ?
oldTree.children[i] : null;
const childDiffTrees = this._generateDiffTree(

View File

@@ -19,7 +19,7 @@
const path = require('path');
const fs = require('fs');
const VueLoaderPlugin = require('vue-loader/lib/plugin');
const { VueLoaderPlugin } = require("vue-loader")
const HtmlWebpackPlugin = require('html-webpack-plugin');
const KotlinWebpackPlugin = require('@jetbrains/kotlin-webpack-plugin');
const HtmlWebpackInlineSourcePlugin =
@@ -129,7 +129,7 @@ const webpackConfig = {
inlineSource: isDev ? false : '.(js|css)',
template: 'src/index_template.html',
}),
new HtmlWebpackInlineSourcePlugin(),
new HtmlWebpackInlineSourcePlugin(HtmlWebpackPlugin),
new KotlinWebpackPlugin({
src: [
path.join(__dirname, '../../../platform_testing/libraries/flicker/' +

File diff suppressed because it is too large Load Diff