[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 start\necho "WM trace started."',
'su root cmd window tracing stop >/dev/null 2>&1' '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( "layers_trace": TraceTarget(
File("/data/misc/wmtrace/layers_trace.pb", "layers_trace"), File("/data/misc/wmtrace/layers_trace.pb", "layers_trace"),
'su root service call SurfaceFlinger 1025 i32 1\necho "SF trace started."', 'su root service call SurfaceFlinger 1025 i32 1\necho "SF trace started."',

View File

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

View File

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

View File

@@ -13,6 +13,7 @@
limitations under the License. limitations under the License.
--> -->
<template> <template>
<div @dragleave="fileDragOut" @dragover="fileDragIn" @drop="handleFileDrop">
<flat-card style="min-width: 50em"> <flat-card style="min-width: 50em">
<md-card-header> <md-card-header>
<div class="md-title">Open files</div> <div class="md-title">Open files</div>
@@ -37,16 +38,6 @@
md-mode="indeterminate" md-mode="indeterminate"
v-show="loadingFiles" 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">
<div class="md-layout-item md-small-size-100"> <div class="md-layout-item md-small-size-100">
<md-field> <md-field>
@@ -100,7 +91,7 @@
:md-active.sync="showSnackbar" :md-active.sync="showSnackbar"
md-persistent md-persistent
> >
<span style="white-space: pre-line;">{{ snackbarText }}</span> <p class="snackbar-break-words">{{ snackbarText }}</p>
<div @click="hideSnackbarMessage()"> <div @click="hideSnackbarMessage()">
<md-button class="md-icon-button"> <md-button class="md-icon-button">
<md-icon style="color: white">close</md-icon> <md-icon style="color: white">close</md-icon>
@@ -108,6 +99,7 @@
</div> </div>
</md-snackbar> </md-snackbar>
</flat-card> </flat-card>
</div>
</template> </template>
<script> <script>
import FlatCard from './components/FlatCard.vue'; import FlatCard from './components/FlatCard.vue';
@@ -145,7 +137,7 @@ export default {
}, },
methods: { methods: {
showSnackbarMessage(message, duration) { showSnackbarMessage(message, duration) {
this.snackbarText = message; this.snackbarText = '\n' + message + '\n';
this.snackbarDuration = duration; this.snackbarDuration = duration;
this.showSnackbar = true; 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) { onLoadFile(e) {
const files = event.target.files || event.dataTransfer.files; const files = event.target.files || event.dataTransfer.files;
this.processFiles(files); this.processFiles(files);

View File

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

View File

@@ -15,24 +15,31 @@
<template> <template>
<md-card-content class="container"> <md-card-content class="container">
<div class="navigation"> <div class="navigation">
<md-button <md-content
class="md-dense md-primary" md-tag="md-toolbar"
@click.native="scrollToRow(lastOccuredVisibleIndex)" md-elevation="0"
class="card-toolbar md-transparent md-dense"
> >
Jump to latest entry <h2 class="md-title" style="flex: 1">Log View</h2>
</md-button> <md-button
<md-button class="md-dense md-primary"
class="md-icon-button" :class="{'md-primary': pinnedToLatest}" @click.native="scrollToRow(lastOccuredVisibleIndex)"
@click.native="togglePin" >
> Jump to latest entry
<md-icon>push_pin</md-icon> </md-button>
<md-tooltip md-direction="top" v-if="pinnedToLatest"> <md-button
Unpin to latest message class="md-icon-button" :class="{'md-primary': pinnedToLatest}"
</md-tooltip> @click.native="togglePin"
<md-tooltip md-direction="top" v-else> >
Pin to latest message <md-icon>push_pin</md-icon>
</md-tooltip> <md-tooltip md-direction="top" v-if="pinnedToLatest">
</md-button> Unpin to latest message
</md-tooltip>
<md-tooltip md-direction="top" v-else>
Pin to latest message
</md-tooltip>
</md-button>
</md-content>
</div> </div>
<div class="filters"> <div class="filters">

View File

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

View File

@@ -67,6 +67,15 @@
</div> </div>
<div class="active-timeline" v-show="minimized"> <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 <div
class="active-timeline-icon" class="active-timeline-icon"
@click="$refs.navigationTypeSelection.$el @click="$refs.navigationTypeSelection.$el
@@ -81,8 +90,9 @@
</div> </div>
<md-field <md-field
v-if="multipleTraces"
ref="navigationTypeSelection" ref="navigationTypeSelection"
class="nagivation-style-selection-field" class="navigation-style-selection-field"
> >
<label>Navigation</label> <label>Navigation</label>
@@ -91,7 +101,8 @@
name="navigationStyle" name="navigationStyle"
md-dense md-dense
> >
<md-icon-option :value="NAVIGATION_STYLE.GLOBAL" <md-icon-option
:value="NAVIGATION_STYLE.GLOBAL"
icon="public" icon="public"
desc="Consider all timelines for navigation" desc="Consider all timelines for navigation"
/> />
@@ -265,7 +276,7 @@ import {NAVIGATION_STYLE} from './utils/consts';
import {TRACE_ICONS} from '@/decode.js'; import {TRACE_ICONS} from '@/decode.js';
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
import {nanos_to_string} from './transform.js'; import {nanos_to_string, string_to_nanos} from './transform.js';
export default { export default {
name: 'overlay', name: 'overlay',
@@ -290,6 +301,7 @@ export default {
crop: null, crop: null,
cropIntent: null, cropIntent: null,
TRACE_ICONS, TRACE_ICONS,
searchTimestamp: '',
}; };
}, },
created() { created() {
@@ -376,7 +388,7 @@ export default {
default: default:
const split = this.navigationStyle.split('-'); const split = this.navigationStyle.split('-');
if (split[0] !== NAVIGATION_STYLE.TARGETED) { if (split[0] !== NAVIGATION_STYLE.TARGETED) {
throw new Error('Unexpected nagivation type'); throw new Error('Unexpected navigation type');
} }
const fileType = split[1]; const fileType = split[1];
@@ -398,7 +410,7 @@ export default {
default: default:
const split = this.navigationStyle.split('-'); const split = this.navigationStyle.split('-');
if (split[0] !== NAVIGATION_STYLE.TARGETED) { if (split[0] !== NAVIGATION_STYLE.TARGETED) {
throw new Error('Unexpected nagivation type'); throw new Error('Unexpected navigation type');
} }
const fileType = split[1]; const fileType = split[1];
@@ -412,7 +424,11 @@ export default {
} }
if (this.navigationStyle === NAVIGATION_STYLE.FOCUSED) { if (this.navigationStyle === NAVIGATION_STYLE.FOCUSED) {
return this.focusedFile; //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) { if (this.navigationStyle === NAVIGATION_STYLE.CUSTOM) {
@@ -425,12 +441,15 @@ export default {
.traces[this.navigationStyle.split('-')[1]]; .traces[this.navigationStyle.split('-')[1]];
} }
throw new Error('Unexpected Nagivation Style'); throw new Error('Unexpected Navigation Style');
}, },
isCropped() { isCropped() {
return this.crop != null && return this.crop != null &&
(this.crop.left !== 0 || this.crop.right !== 1); (this.crop.left !== 0 || this.crop.right !== 1);
}, },
multipleTraces() {
return this.timelineFiles.length > 1;
},
}, },
updated() { updated() {
this.$nextTick(() => { this.$nextTick(() => {
@@ -589,7 +608,7 @@ export default {
default: default:
const split = this.navigationStyle.split('-'); const split = this.navigationStyle.split('-');
if (split[0] !== NAVIGATION_STYLE.TARGETED) { if (split[0] !== NAVIGATION_STYLE.TARGETED) {
throw new Error('Unexpected nagivation type'); throw new Error('Unexpected navigation type');
} }
const fileType = split[1]; const fileType = split[1];
@@ -623,6 +642,17 @@ export default {
clearSelection() { clearSelection() {
this.crop = null; 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: { components: {
'timeline': Timeline, 'timeline': Timeline,
@@ -816,7 +846,7 @@ export default {
margin-top: 4px; margin-top: 4px;
} }
.nagivation-style-selection-field { .navigation-style-selection-field {
width: 90px; width: 90px;
margin-right: 10px; margin-right: 10px;
margin-bottom: 0; margin-bottom: 0;

View File

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

View File

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

View File

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

View File

@@ -33,14 +33,21 @@
v-for="(surface, index) in sufacesAffectedBy(source)" v-for="(surface, index) in sufacesAffectedBy(source)"
v-bind:key="surface.id" v-bind:key="surface.id"
> >
<!-- eslint-disable-next-line max-len --> <span
<span v-if="surface.name" class="surface-name">{{ surface.name }}</span> v-if="simplifyNames && surface.shortName &&
<span class="surface-id"> surface.shortName !== surface.name"
<!-- eslint-disable-next-line max-len --> >{{surface.shortName}}>
<span v-if="surface.name">(</span>{{surface.id}}<span v-if="surface.name">)</span> </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">
<!-- eslint-disable-next-line max-len -->
<span v-if="surface.name">(</span>{{surface.id}}<span v-if="surface.name">)</span>
</span>
<!-- eslint-disable-next-line max-len -->
<span v-if="index + 1 < sufacesAffectedBy(source).length">,&nbsp;</span>
</span> </span>
<!-- eslint-disable-next-line max-len -->
<span v-if="index + 1 < sufacesAffectedBy(source).length">,&nbsp;</span>
</span> </span>
</div> </div>
<div class="extra-info-column"> <div class="extra-info-column">
@@ -59,6 +66,8 @@
</template> </template>
<script> <script>
import { shortenName } from './flickerlib/mixin'
export default { export default {
name: 'transaction-entry', name: 'transaction-entry',
props: { props: {
@@ -83,6 +92,9 @@ export default {
prettifyTransactionId: { prettifyTransactionId: {
type: Function, type: Function,
}, },
simplifyNames: {
type: Boolean,
},
}, },
computed: { computed: {
currentTimestamp() { currentTimestamp() {
@@ -135,8 +147,12 @@ export default {
}, },
sufacesAffectedBy(transaction) { sufacesAffectedBy(transaction) {
if (transaction.type !== 'transaction') { if (transaction.type !== 'transaction') {
// TODO (b/162402459): Shorten layer name return [
return [{name: transaction.layerName, id: transaction.obj.id}]; {
name: transaction.layerName,
shortName: shortenName(transaction.layerName),
id: transaction.obj.id
}];
} }
const surfaceIds = new Set(); const surfaceIds = new Set();
@@ -145,7 +161,12 @@ export default {
const id = transaction.obj.id; const id = transaction.obj.id;
if (!surfaceIds.has(id)) { if (!surfaceIds.has(id)) {
surfaceIds.add(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"> <md-card-content class="container">
<flat-card class="changes card"> <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="filters">
<div class="input"> <div class="input">
<md-field> <md-field>
@@ -75,6 +82,11 @@
<div class="md-helper-text">Press enter to add</div> <div class="md-helper-text">Press enter to add</div>
</md-chips> </md-chips>
</div> </div>
<md-checkbox v-model="trace.simplifyNames">
Simplify names
</md-checkbox>
</div> </div>
<virtual-list style="height: 600px; overflow-y: auto;" <virtual-list style="height: 600px; overflow-y: auto;"
@@ -86,6 +98,7 @@
selectedTransaction, selectedTransaction,
transactionsTrace, transactionsTrace,
prettifyTransactionId, prettifyTransactionId,
simplifyNames: trace.simplifyNames,
}" }"
ref="loglist" ref="loglist"
/> />
@@ -289,7 +302,6 @@ export default {
const perpareForTreeViewTransform = (change) => { const perpareForTreeViewTransform = (change) => {
this.removeNullFields(change); this.removeNullFields(change);
change[META_DATA_KEY] = { change[META_DATA_KEY] = {
// TODO (b/162402459): Shorten layer name
layerName: change.layerName, layerName: change.layerName,
}; };
// remove redundant properties // remove redundant properties

View File

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

View File

@@ -30,8 +30,8 @@ export default {
summarizer(item) { summarizer(item) {
const summary = []; const summary = [];
if (item.obj.isIncompleteReason) { if (item.isIncompleteReason) {
summary.push({key: 'Incomplete state reason', value: item.obj.isIncompleteReason}); summary.push({key: 'Incomplete state reason', value: item.isIncompleteReason});
} }
return summary; 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 camelcase */
/* eslint-disable max-len */ /* 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 jsonProtoDefsWm from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto';
import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/internal/protolog.proto'; import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/internal/protolog.proto';
import jsonProtoDefsSf from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.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 jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto';
import jsonProtoDefsIme from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto'; import jsonProtoDefsIme from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto';
import protobuf from 'protobufjs'; 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_transaction_trace} from './transform_transaction.js';
import {transform_wl_outputstate, transform_wayland_trace} from './transform_wl.js'; import {transform_wl_outputstate, transform_wayland_trace} from './transform_wl.js';
import {transformProtolog} from './transform_protolog.js'; import {transformProtolog} from './transform_protolog.js';
import {transform_sysui_trace} from './transform_sys_ui.js'; import {transform_sysui_trace} from './transform_sys_ui.js';
import {transform_launcher_trace} from './transform_launcher.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 {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 {mp4Decoder} from './decodeVideo.js';
import AccessibilityTrace from '@/traces/Accessibility.ts';
import SurfaceFlingerTrace from '@/traces/SurfaceFlinger.ts'; import SurfaceFlingerTrace from '@/traces/SurfaceFlinger.ts';
import WindowManagerTrace from '@/traces/WindowManager.ts'; import WindowManagerTrace from '@/traces/WindowManager.ts';
import TransactionsTrace from '@/traces/Transactions.ts'; import TransactionsTrace from '@/traces/Transactions.ts';
@@ -52,6 +53,7 @@ import SurfaceFlingerDump from '@/dumps/SurfaceFlinger.ts';
import WindowManagerDump from '@/dumps/WindowManager.ts'; import WindowManagerDump from '@/dumps/WindowManager.ts';
import WaylandDump from '@/dumps/Wayland.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 WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerTraceFileProto');
const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto'); const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto');
const SfTraceMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersTraceFileProto'); 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 ProtoLogMessage = lookup_type(jsonProtoDefsProtoLog, 'com.android.internal.protolog.ProtoLogFileProto');
const SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, 'com.android.systemui.tracing.SystemUiTraceFileProto'); const SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, 'com.android.systemui.tracing.SystemUiTraceFileProto');
const LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, 'com.android.launcher3.tracing.LauncherTraceFileProto'); const LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, 'com.android.launcher3.tracing.LauncherTraceFileProto');
const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodClientsTraceFileProto"); const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodClientsTraceFileProto');
const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodServiceTraceFileProto"); const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, 'android.view.inputmethod.InputMethodServiceTraceFileProto');
const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodManagerServiceTraceFileProto"); 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 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 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 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 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 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 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 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 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 IMM_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45]; // .IMMTRACE
const FILE_TYPES = Object.freeze({ const FILE_TYPES = Object.freeze({
ACCESSIBILITY_TRACE: 'AccessibilityTrace',
WINDOW_MANAGER_TRACE: 'WindowManagerTrace', WINDOW_MANAGER_TRACE: 'WindowManagerTrace',
SURFACE_FLINGER_TRACE: 'SurfaceFlingerTrace', SURFACE_FLINGER_TRACE: 'SurfaceFlingerTrace',
WINDOW_MANAGER_DUMP: 'WindowManagerDump', WINDOW_MANAGER_DUMP: 'WindowManagerDump',
@@ -103,8 +107,10 @@ const PROTO_LOG_ICON = 'notes';
const SYSTEM_UI_ICON = 'filter_none'; const SYSTEM_UI_ICON = 'filter_none';
const LAUNCHER_ICON = 'filter_none'; const LAUNCHER_ICON = 'filter_none';
const IME_ICON = 'keyboard'; const IME_ICON = 'keyboard';
const ACCESSIBILITY_ICON = 'filter_none';
const FILE_ICONS = { const FILE_ICONS = {
[FILE_TYPES.ACCESSIBILITY_TRACE]: ACCESSIBILITY_ICON,
[FILE_TYPES.WINDOW_MANAGER_TRACE]: WINDOW_MANAGER_ICON, [FILE_TYPES.WINDOW_MANAGER_TRACE]: WINDOW_MANAGER_ICON,
[FILE_TYPES.SURFACE_FLINGER_TRACE]: SURFACE_FLINGER_ICON, [FILE_TYPES.SURFACE_FLINGER_TRACE]: SURFACE_FLINGER_ICON,
[FILE_TYPES.WINDOW_MANAGER_DUMP]: WINDOW_MANAGER_ICON, [FILE_TYPES.WINDOW_MANAGER_DUMP]: WINDOW_MANAGER_ICON,
@@ -125,11 +131,8 @@ function oneOf(dataType) {
return {oneOf: true, type: dataType}; return {oneOf: true, type: dataType};
} }
function manyOf(dataType, fold = null) {
return {manyOf: true, type: dataType, fold};
}
const TRACE_TYPES = Object.freeze({ const TRACE_TYPES = Object.freeze({
ACCESSIBILITY: 'AccessibilityTrace',
WINDOW_MANAGER: 'WindowManagerTrace', WINDOW_MANAGER: 'WindowManagerTrace',
SURFACE_FLINGER: 'SurfaceFlingerTrace', SURFACE_FLINGER: 'SurfaceFlingerTrace',
SCREEN_RECORDING: 'ScreenRecording', SCREEN_RECORDING: 'ScreenRecording',
@@ -144,6 +147,12 @@ const TRACE_TYPES = Object.freeze({
}); });
const TRACE_INFO = { const TRACE_INFO = {
[TRACE_TYPES.ACCESSIBILITY]: {
name: 'Accessibility',
icon: ACCESSIBILITY_ICON,
files: [oneOf(FILE_TYPES.ACCESSIBILITY_TRACE)],
constructor: AccessibilityTrace,
},
[TRACE_TYPES.WINDOW_MANAGER]: { [TRACE_TYPES.WINDOW_MANAGER]: {
name: 'WindowManager', name: 'WindowManager',
icon: WINDOW_MANAGER_ICON, icon: WINDOW_MANAGER_ICON,
@@ -261,6 +270,16 @@ export const TRACE_ICONS = {
// TODO: Rename name to defaultName // TODO: Rename name to defaultName
const FILE_DECODERS = { 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]: { [FILE_TYPES.WINDOW_MANAGER_TRACE]: {
name: 'WindowManager trace', name: 'WindowManager trace',
decoder: protoDecoder, decoder: protoDecoder,
@@ -278,7 +297,7 @@ const FILE_DECODERS = {
type: FILE_TYPES.SURFACE_FLINGER_TRACE, type: FILE_TYPES.SURFACE_FLINGER_TRACE,
mime: 'application/octet-stream', mime: 'application/octet-stream',
protoType: SfTraceMessage, protoType: SfTraceMessage,
transform: transformLayersTrace, transform: SurfaceFlingerTrace.fromProto,
timeline: true, timeline: true,
}, },
}, },
@@ -300,8 +319,8 @@ const FILE_DECODERS = {
type: FILE_TYPES.SURFACE_FLINGER_DUMP, type: FILE_TYPES.SURFACE_FLINGER_DUMP,
mime: 'application/octet-stream', mime: 'application/octet-stream',
protoType: SfDumpMessage, protoType: SfDumpMessage,
transform: (decoded) => transformLayers(true /* includesCompositionState*/, decoded), transform: SurfaceFlingerDump.fromProto,
timeline: false, timeline: true,
}, },
}, },
[FILE_TYPES.WINDOW_MANAGER_DUMP]: { [FILE_TYPES.WINDOW_MANAGER_DUMP]: {
@@ -312,7 +331,7 @@ const FILE_DECODERS = {
mime: 'application/octet-stream', mime: 'application/octet-stream',
protoType: WmDumpMessage, protoType: WmDumpMessage,
transform: WindowManagerDump.fromProto, transform: WindowManagerDump.fromProto,
timeline: false, timeline: true,
}, },
}, },
[FILE_TYPES.WAYLAND_DUMP]: { [FILE_TYPES.WAYLAND_DUMP]: {
@@ -323,7 +342,7 @@ const FILE_DECODERS = {
mime: 'application/octet-stream', mime: 'application/octet-stream',
protoType: WaylandDumpMessage, protoType: WaylandDumpMessage,
transform: transform_wl_outputstate, transform: transform_wl_outputstate,
timeline: false, timeline: true,
}, },
}, },
[FILE_TYPES.SCREEN_RECORDING]: { [FILE_TYPES.SCREEN_RECORDING]: {
@@ -443,11 +462,6 @@ function modifyProtoFields(protoObj, displayDefaults) {
protoObj[fieldName] = fieldProperties.defaultValue; protoObj[fieldName] = fieldProperties.defaultValue;
} }
if (fieldProperties.type === 'TransformProto') {
fill_transform_data(protoObj[fieldName]);
continue;
}
if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) { if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]]; protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
continue; continue;
@@ -524,6 +538,9 @@ function detectAndDecode(buffer, fileName, store) {
if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) { if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) {
return decodedFile(FILE_TYPES.SURFACE_FLINGER_TRACE, buffer, fileName, store); 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)) { if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) {
return decodedFile(FILE_TYPES.WINDOW_MANAGER_TRACE, buffer, fileName, store); 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 { FILE_TYPES, DUMP_TYPES } from "@/decode.js";
import DumpBase from "./DumpBase"; import DumpBase from "./DumpBase";
import LayersTraceEntry from '../flickerlib/layers/LayerTraceEntry'
export default class SurfaceFlinger extends DumpBase { export default class SurfaceFlinger extends DumpBase {
sfDumpFile: any; sfDumpFile: any;
@@ -30,4 +31,12 @@ export default class SurfaceFlinger extends DumpBase {
get type() { get type() {
return DUMP_TYPES.SURFACE_FLINGER; 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; return DUMP_TYPES.WINDOW_MANAGER;
} }
static fromProto(proto): WindowManagerTrace { static fromProto(proto: any): WindowManagerTrace {
return WindowManagerTrace.fromDump(proto); 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. * limitations under the License.
*/ */
import {toBounds, toBuffer, toColor, toPoint, toRect, import {toSize, toBuffer, toColor, toPoint, toRect,
toRectF, toRegion, toTransform} from './common'; toRectF, toRegion, toTransform} from './common';
import intDefMapping from import intDefMapping from
'../../../../../prebuilts/misc/common/winscope/intDefMapping.json'; '../../../../../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 { export default class ObjectFormatter {
private static INVALID_ELEMENT_PROPERTIES = ['length', 'name', 'prototype', 'children', static displayDefaults: boolean = false
'childrenWindows', 'ref', 'root', 'layers', 'resolvedChildren'] private static INVALID_ELEMENT_PROPERTIES = config.invalidProperties;
private static FLICKER_INTDEF_MAP = new Map([ private static FLICKER_INTDEF_MAP = readIntdefMap();
[`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`], static cloneObject(entry: any): any {
[`WindowConfiguration.windowingMode`, `android.app.WindowConfiguration.WindowingMode`], let obj: any = {}
[`Configuration.orientation`, `android.content.pm.ActivityInfo.ScreenOrientation`], const properties = ObjectFormatter.getProperties(entry);
[`WindowConfiguration.orientation`, `android.content.pm.ActivityInfo.ScreenOrientation`], properties.forEach(prop => obj[prop] = entry[prop]);
[`WindowState.orientation`, `android.content.pm.ActivityInfo.ScreenOrientation`], 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): {} { static format(obj: any): {} {
const entries = Object.entries(obj) const properties = this.getProperties(obj);
.filter(it => !it[0].includes(`$`)) const sortedProperties = properties.sort()
.filter(it => !this.INVALID_ELEMENT_PROPERTIES.includes(it[0]))
const sortedEntries = entries.sort()
const result: any = {} const result: any = {}
sortedEntries.forEach(entry => { sortedProperties.forEach(entry => {
const key = entry[0] const key = entry;
const value: any = entry[1] const value: any = obj[key];
if (value) { if (value || (this.displayDefaults && value !== undefined && value !== null)) {
// flicker obj // flicker obj
if (value.prettyPrint) { if (value.prettyPrint) {
result[key] = value.prettyPrint() const isEmpty = value.isEmpty === true;
if (!isEmpty || this.displayDefaults) {
result[key] = value.prettyPrint()
}
} else { } else {
// converted proto to flicker // converted proto to flicker
const translatedObject = this.translateObject(value) const translatedObject = this.translateObject(value)
@@ -63,7 +99,11 @@ export default class ObjectFormatter {
result[key] = translatedObject.prettyPrint() result[key] = translatedObject.prettyPrint()
// objects - recursive call // objects - recursive call
} else if (value && typeof(value) == `object`) { } 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 { } else {
// values // values
result[key] = this.translateIntDef(obj, key, value) 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) { private static translateObject(obj) {
const type = obj?.$type?.name const type = obj?.$type?.name
switch(type) { switch(type) {
case `SizeProto`: return toBounds(obj) case `SizeProto`: return toSize(obj)
case `ActiveBufferProto`: return toBuffer(obj) case `ActiveBufferProto`: return toBuffer(obj)
case `ColorProto`: return toColor(obj) case `ColorProto`: return toColor(obj)
case `PointProto`: return toPoint(obj) case `PointProto`: return toPoint(obj)

View File

@@ -14,9 +14,6 @@
* limitations under the License. * limitations under the License.
*/ */
import { asRawTreeViewObject } from '../utils/diff.js'
import { getPropertiesForDisplay } from './mixin'
import { import {
KeyguardControllerState, KeyguardControllerState,
RootWindowContainer, RootWindowContainer,
@@ -26,17 +23,20 @@ import {
import WindowContainer from "./windows/WindowContainer" import WindowContainer from "./windows/WindowContainer"
WindowManagerState.fromProto = function ({proto, timestamp = 0, where = ""}): WindowManagerState { WindowManagerState.fromProto = function (proto: any, timestamp: number = 0, where: string = ""): WindowManagerState {
var inputMethodWIndowAppToken = "" var inputMethodWIndowAppToken = "";
if (proto.inputMethodWindow != null) { if (proto.inputMethodWindow != null) {
proto.inputMethodWindow.hashCode.toString(16) proto.inputMethodWindow.hashCode.toString(16)
} };
const rootWindowContainer = newRootWindowContainer(proto.rootWindowContainer)
const keyguardControllerState = newKeyguardControllerState( const rootWindowContainer = createRootWindowContainer(proto.rootWindowContainer);
proto.rootWindowContainer.keyguardController) const keyguardControllerState = createKeyguardControllerState(
proto.rootWindowContainer.keyguardController);
const policy = createWindowManagerPolicy(proto.policy);
const entry = new WindowManagerState( const entry = new WindowManagerState(
where, where,
newWindowManagerPolicy(proto.policy), policy,
proto.focusedApp, proto.focusedApp,
proto.focusedDisplayId, proto.focusedDisplayId,
proto.focusedWindow?.title ?? "", proto.focusedWindow?.title ?? "",
@@ -47,26 +47,30 @@ WindowManagerState.fromProto = function ({proto, timestamp = 0, where = ""}): Wi
rootWindowContainer, rootWindowContainer,
keyguardControllerState, keyguardControllerState,
timestamp = timestamp 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) console.warn("Created ", entry.kind, " stableId=", entry.stableId)
return entry 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( return new WindowManagerPolicy(
proto.focusedAppToken || "", proto.focusedAppToken ?? "",
proto.forceStatusBar, proto.forceStatusBar,
proto.forceStatusBarFromKeyguard, proto.forceStatusBarFromKeyguard,
proto.keyguardDrawComplete, proto.keyguardDrawComplete,
@@ -79,36 +83,35 @@ function newWindowManagerPolicy(proto): WindowManagerPolicy {
proto.rotationMode, proto.rotationMode,
proto.screenOnFully, proto.screenOnFully,
proto.windowManagerDrawComplete proto.windowManagerDrawComplete
) );
} }
function newRootWindowContainer(proto): RootWindowContainer { function createRootWindowContainer(proto: any): RootWindowContainer {
const children = proto.windowContainer.children.reverse()
.filter(it => it != null)
.map(it => WindowContainer.childrenFromProto(it, /* isActivityInTree */ false))
const windowContainer = WindowContainer.fromProto( const windowContainer = WindowContainer.fromProto(
{proto: proto.windowContainer, children: children}) /* proto */ proto.windowContainer,
if (windowContainer == null) { /* childrenProto */ proto.windowContainer.children.reverse()
throw "Window container should not be null: " + JSON.stringify(proto) );
}
const entry = new RootWindowContainer(windowContainer)
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 { function createKeyguardControllerState(proto: any): KeyguardControllerState {
const keyguardOccludedStates = {} const keyguardOccludedStates = {};
if (proto) { if (proto) {
proto.keyguardOccludedStates.forEach(it => proto.keyguardOccludedStates.forEach(it =>
keyguardOccludedStates[it.displayId] = it.keyguardOccluded) keyguardOccludedStates[it.displayId] = it.keyguardOccluded);
} }
return new KeyguardControllerState( return new KeyguardControllerState(
proto?.isAodShowing ?? false, proto?.isAodShowing ?? false,
proto?.isKeyguardShowing ?? false, proto?.isKeyguardShowing ?? false,
keyguardOccludedStates keyguardOccludedStates
) );
} }
export default WindowManagerState; export default WindowManagerState;

View File

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

View File

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

View File

@@ -14,12 +14,13 @@
* limitations under the License. * limitations under the License.
*/ */
import LayersTrace from './LayersTrace';
import WindowManagerState from './WindowManagerState'; import WindowManagerState from './WindowManagerState';
import WindowManagerTrace from './WindowManagerTrace'; import WindowManagerTrace from './WindowManagerTrace';
import ObjectFormatter from './ObjectFormatter';
/** /**
* Entry point into the flickerlib for Winscope. * Entry point into the flickerlib for Winscope.
* Expose everything we want Winscope to have access to here. * Expose everything we want Winscope to have access to here.
*/ */
export {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 entry WM hierarchy element
* @param proto Associated proto object * @param proto Associated proto object
*/ */
export function getPropertiesForDisplay(proto: any, entry: any): any { export function getPropertiesForDisplay(entry: any): any {
let obj = Object.assign({}, entry) if (!entry) {
if (obj.children) delete obj.children return
// obj = ObjectFormatter.format(obj) }
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.children) delete obj.proto.children
if (obj.proto.childWindows) delete obj.proto.childWindows if (obj.proto.childWindows) delete obj.proto.childWindows
if (obj.proto.childrenWindows) delete obj.proto.childrenWindows if (obj.proto.childrenWindows) delete obj.proto.childrenWindows

View File

@@ -18,3 +18,33 @@ import Chip from "./Chip"
import ChipType from "./ChipType" import ChipType from "./ChipType"
export const VISIBLE_CHIP = new Chip("V", "visible", ChipType.DEFAULT) 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. * limitations under the License.
*/ */
import { getPropertiesForDisplay, shortenName } from '../mixin' import { shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { Activity } from "../common" import { Activity } from "../common"
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
Activity.fromProto = function (proto): Activity { Activity.fromProto = function (proto: any): Activity {
if (proto == null) { if (proto == null) {
return null return null;
} else { } else {
const children = proto.windowToken.windowContainer.children.reverse() const windowContainer = WindowContainer.fromProto(
.filter(it => it != null) /* proto */ proto.windowToken.windowContainer,
.map(it => WindowContainer.childrenFromProto(it, /* isActivityInTree */ true)) /* protoChildren */ proto.windowToken.windowContainer.children.reverse(),
const windowContainer = WindowContainer.fromProto({proto: proto.windowToken.windowContainer, /* isActivityInTree */ true,
children: children, identifierOverride: proto.identifier}) /* nameOverride */ null,
if (windowContainer == null) { /* identifierOverride */ proto.identifier
throw "Window container should not be null: " + JSON.stringify(proto) );
}
const entry = new Activity( const entry = new Activity(
proto.name, proto.name,
proto.state, proto.state,
@@ -39,16 +38,18 @@ Activity.fromProto = function (proto): Activity {
proto.procId, proto.procId,
proto.translucent, proto.translucent,
windowContainer windowContainer
) );
entry.obj = getPropertiesForDisplay(proto, entry) addAttributes(entry, proto);
entry.kind = entry.constructor.name console.warn("Created ", entry.kind, " stableId=", entry.stableId);
entry.shortName = shortenName(entry.name) return entry;
entry.rawTreeViewObject = asRawTreeViewObject(entry)
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. * limitations under the License.
*/ */
import { getPropertiesForDisplay, shortenName } from '../mixin' import { shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { DisplayArea } from "../common" import { DisplayArea } from "../common"
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
DisplayArea.fromProto = function (proto, isActivityInTree: Boolean): DisplayArea { DisplayArea.fromProto = function (proto: any, isActivityInTree: Boolean): DisplayArea {
if (proto == null) { if (proto == null) {
return null return null;
} else { } else {
const children = proto.windowContainer.children.reverse() const windowContainer = WindowContainer.fromProto(
.filter(it => it != null) /* proto */ proto.windowContainer,
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree)) /* protoChildren */ proto.windowContainer.children.reverse(),
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer, /* isActivityInTree */ isActivityInTree,
children: children, nameOverride: proto.name}) /* nameOverride */ proto.name
if (windowContainer == null) { );
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer)
entry.obj = getPropertiesForDisplay(proto, entry) const entry = new DisplayArea(proto.isTaskDisplayArea, windowContainer);
entry.kind = entry.constructor.name
entry.shortName = shortenName(entry.name)
entry.rawTreeViewObject = asRawTreeViewObject(entry)
console.warn("Created ", entry.kind, " stableId=", entry.stableId) addAttributes(entry, proto);
return entry 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. * limitations under the License.
*/ */
import { getPropertiesForDisplay, shortenName } from '../mixin' import { shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { toRect, DisplayContent, Rect } from "../common" import { toRect, DisplayContent, Rect } from "../common"
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
DisplayContent.fromProto = function (proto, isActivityInTree: Boolean): DisplayContent { DisplayContent.fromProto = function (proto: any, isActivityInTree: Boolean): DisplayContent {
if (proto == null) { if (proto == null) {
return null return null;
} else { } else {
const children = proto.rootDisplayArea.windowContainer.children.reverse() const windowContainer = WindowContainer.fromProto(
.filter(it => it != null) /* proto */ proto.rootDisplayArea.windowContainer,
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree)) /* protoChildren */ proto.rootDisplayArea.windowContainer.children.reverse(),
const windowContainer = WindowContainer.fromProto({proto: proto.rootDisplayArea.windowContainer, /* isActivityInTree */ isActivityInTree,
children: children, nameOverride: proto.displayInfo?.name ?? null}) /* 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 displayRectWidth = proto.displayInfo?.logicalWidth ?? 0 const appRectHeight = proto.displayInfo?.appHeight ?? 0;
const displayRectHeight = proto.displayInfo?.logicalHeight ?? 0 const defaultBounds = proto.pinnedStackController?.defaultBounds ?? null;
const appRectWidth = proto.displayInfo?.appWidth ?? 0 const movementBounds = proto.pinnedStackController?.movementBounds ?? null;
const appRectHeight = proto.displayInfo?.appHeight ?? 0
const defaultBounds = proto.pinnedStackController?.defaultBounds ?? null
const movementBounds = proto.pinnedStackController?.movementBounds ?? null
const entry = new DisplayContent( const entry = new DisplayContent(
proto.id, proto.id,
@@ -59,16 +54,18 @@ DisplayContent.fromProto = function (proto, isActivityInTree: Boolean): DisplayC
proto.displayRotation?.rotation ?? 0, proto.displayRotation?.rotation ?? 0,
proto.displayRotation?.lastOrientation ?? 0, proto.displayRotation?.lastOrientation ?? 0,
windowContainer windowContainer
) );
entry.obj = getPropertiesForDisplay(proto, entry) addAttributes(entry, proto);
entry.kind = entry.constructor.name console.warn("Created ", entry.kind, " stableId=", entry.stableId);
entry.shortName = shortenName(entry.name) return entry;
entry.rawTreeViewObject = asRawTreeViewObject(entry)
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. * limitations under the License.
*/ */
import { getPropertiesForDisplay, shortenName } from '../mixin' import { shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { Task, toRect } from "../common" import { Task, toRect } from "../common"
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
Task.fromProto = function (proto, isActivityInTree: Boolean): Task { Task.fromProto = function (proto: any, isActivityInTree: Boolean): Task {
if (proto == null) { if (proto == null) {
return null return null;
} else { } else {
const windowContainerProto = proto.taskFragment?.windowContainer ?? proto.windowContainer const windowContainerProto = proto.taskFragment?.windowContainer ?? proto.windowContainer;
const children = windowContainerProto.children.reverse() const windowContainer = WindowContainer.fromProto(
.filter(it => it != null) /* proto */ windowContainerProto,
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree)) /* protoChildren */ windowContainerProto.children.reverse(),
const windowContainer = WindowContainer.fromProto({proto: windowContainerProto, /* isActivityInTree */ isActivityInTree
children: children}) );
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new Task( const entry = new Task(
proto.taskFragment?.activityType ?? proto.activityType, proto.taskFragment?.activityType ?? proto.activityType,
proto.fillsParent, proto.fillsParent,
@@ -51,16 +48,18 @@ Task.fromProto = function (proto, isActivityInTree: Boolean): Task {
proto.taskFragment?.minWidth ?? proto.minWidth, proto.taskFragment?.minWidth ?? proto.minWidth,
proto.taskFragment?.minHeight ?? proto.minHeight, proto.taskFragment?.minHeight ?? proto.minHeight,
windowContainer windowContainer
) );
entry.obj = getPropertiesForDisplay(proto, entry) addAttributes(entry, proto);
entry.kind = entry.constructor.name console.warn("Created ", entry.kind, " stableId=", entry.stableId);
entry.shortName = shortenName(entry.name) return entry;
entry.rawTreeViewObject = asRawTreeViewObject(entry)
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. * limitations under the License.
*/ */
import { getPropertiesForDisplay, shortenName } from '../mixin' import { shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { TaskFragment } from "../common" import { TaskFragment } from "../common"
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
TaskFragment.fromProto = function (proto, isActivityInTree: Boolean): TaskFragment { TaskFragment.fromProto = function (proto: any, isActivityInTree: Boolean): TaskFragment {
if (proto == null) { if (proto == null) {
return null return null;
} else { } else {
const children = proto.windowContainer.children.reverse() const windowContainer = WindowContainer.fromProto(
.filter(it => it != null) /* proto */ proto.windowContainer,
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree)) /* protoChildren */ proto.windowContainer.children.reverse(),
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer, /* isActivityInTree */ isActivityInTree);
children: children})
if (windowContainer == null) {
throw "Window container should not be null: " + JSON.stringify(proto)
}
const entry = new TaskFragment( const entry = new TaskFragment(
proto.activityType, proto.activityType,
proto.displayId, proto.displayId,
proto.minWidth, proto.minWidth,
proto.minHeight, proto.minHeight,
windowContainer windowContainer
) );
entry.obj = getPropertiesForDisplay(proto, entry) addAttributes(entry, proto);
entry.kind = entry.constructor.name console.warn("Created ", entry.kind, " stableId=", entry.stableId);
entry.shortName = shortenName(entry.name) return entry;
entry.rawTreeViewObject = asRawTreeViewObject(entry)
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. * limitations under the License.
*/ */
import { getPropertiesForDisplay, shortenName } from '../mixin' import { shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { import {
Configuration, Configuration,
@@ -34,21 +33,27 @@ import TaskFragment from "./TaskFragment"
import WindowState from "./WindowState" import WindowState from "./WindowState"
import WindowToken from "./WindowToken" import WindowToken from "./WindowToken"
WindowContainer.fromProto = function ({ WindowContainer.fromProto = function (
proto, proto: any,
children, protoChildren: any[],
nameOverride = null, isActivityInTree: boolean,
identifierOverride = null, nameOverride: string = null,
tokenOverride = null identifierOverride: string = null,
}): WindowContainer { tokenOverride = null,
): WindowContainer {
if (proto == null) { 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( const entry = new WindowContainer(
name, name,
token, token,
@@ -56,19 +61,19 @@ WindowContainer.fromProto = function ({
proto.visible, proto.visible,
config, config,
children children
) );
// we remove the children property from the object to avoid it showing the addAttributes(entry, proto);
// the properties view of the element as we can always see those elements' return entry;
// 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
} }
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) ?? return DisplayContent.fromProto(proto.displayContent, isActivityInTree) ??
DisplayArea.fromProto(proto.displayArea, isActivityInTree) ?? DisplayArea.fromProto(proto.displayArea, isActivityInTree) ??
Task.fromProto(proto.task, isActivityInTree) ?? Task.fromProto(proto.task, isActivityInTree) ??
@@ -76,28 +81,28 @@ WindowContainer.childrenFromProto = function(proto, isActivityInTree: Boolean):
Activity.fromProto(proto.activity) ?? Activity.fromProto(proto.activity) ??
WindowToken.fromProto(proto.windowToken, isActivityInTree) ?? WindowToken.fromProto(proto.windowToken, isActivityInTree) ??
WindowState.fromProto(proto.window, 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( const entry = new ConfigurationContainer(
newConfiguration(proto?.overrideConfiguration ?? null), createConfiguration(proto?.overrideConfiguration ?? null),
newConfiguration(proto?.fullConfiguration ?? null), createConfiguration(proto?.fullConfiguration ?? null),
newConfiguration(proto?.mergedOverrideConfiguration ?? null) createConfiguration(proto?.mergedOverrideConfiguration ?? null)
) );
entry.obj = entry entry.obj = entry;
return entry return entry;
} }
function newConfiguration(proto): Configuration { function createConfiguration(proto: any): Configuration {
if (proto == null) { if (proto == null) {
return null return null;
} }
var windowConfiguration = null var windowConfiguration = null;
if (proto != null && proto.windowConfiguration != null) { if (proto != null && proto.windowConfiguration != null) {
windowConfiguration = newWindowConfiguration(proto.windowConfiguration) windowConfiguration = createWindowConfiguration(proto.windowConfiguration);
} }
return new Configuration( return new Configuration(
@@ -109,17 +114,17 @@ function newConfiguration(proto): Configuration {
proto?.smallestScreenWidthDp ?? 0, proto?.smallestScreenWidthDp ?? 0,
proto?.screenLayout ?? 0, proto?.screenLayout ?? 0,
proto?.uiMode ?? 0 proto?.uiMode ?? 0
) );
} }
function newWindowConfiguration(proto): WindowConfiguration { function createWindowConfiguration(proto: any): WindowConfiguration {
return new WindowConfiguration( return new WindowConfiguration(
toRect(proto.appBounds), toRect(proto.appBounds),
toRect(proto.bounds), toRect(proto.bounds),
toRect(proto.maxBounds), toRect(proto.maxBounds),
proto.windowingMode, proto.windowingMode,
proto.activityType proto.activityType
) );
} }
export default WindowContainer export default WindowContainer;

View File

@@ -14,55 +14,35 @@
* limitations under the License. * limitations under the License.
*/ */
import { getPropertiesForDisplay, shortenName } from '../mixin' import { shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js' import { toRect, Size, WindowState, WindowLayoutParams } from "../common"
import { toRect, Bounds, WindowState, WindowLayoutParams } from "../common"
import { VISIBLE_CHIP } from '../treeview/Chips' import { VISIBLE_CHIP } from '../treeview/Chips'
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
WindowState.fromProto = function (proto, isActivityInTree: Boolean): WindowState { WindowState.fromProto = function (proto: any, isActivityInTree: Boolean): WindowState {
if (proto == null) { if (proto == null) {
return null return null;
} else { } else {
const identifierName = proto.windowContainer.identifier?.title ?? proto.identifier?.title ?? "" const windowParams = createWindowLayoutParams(proto.attributes);
var windowType = 0 const identifierName = getIdentifier(proto);
if (identifierName.startsWith(WindowState.STARTING_WINDOW_PREFIX)) { const windowType = getWindowType(proto, identifierName);
windowType = WindowState.WINDOW_TYPE_STARTING const name = getName(identifierName);
} else if (proto.animatingExit) { const windowContainer = WindowContainer.fromProto(
windowType = WindowState.WINDOW_TYPE_EXITING /* proto */ proto.windowContainer,
} else if (identifierName.startsWith(WindowState.DEBUGGER_WINDOW_PREFIX)) { /* protoChildren */ proto.windowContainer.children.reverse(),
windowType = WindowState.WINDOW_TYPE_STARTING /* isActivityInTree */ isActivityInTree,
} /* nameOverride */ name,
/* identifierOverride */ proto.identifier
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 entry = new WindowState( const entry = new WindowState(
newWindowLayoutParams(proto.attributes), windowParams,
proto.displayId, proto.displayId,
proto.stackId, proto.stackId,
proto.animator?.surface?.layer ?? 0, proto.animator?.surface?.layer ?? 0,
proto.animator?.surface?.shown ?? false, proto.animator?.surface?.shown ?? false,
windowType, windowType,
new Bounds(proto.requestedWidth, proto.requestedHeight), new Size(proto.requestedWidth, proto.requestedHeight),
toRect(proto.surfacePosition), toRect(proto.surfacePosition),
toRect(proto.windowFrames?.frame ?? null), toRect(proto.windowFrames?.frame ?? null),
toRect(proto.windowFrames?.containingFrame ?? null), toRect(proto.windowFrames?.containingFrame ?? null),
@@ -74,22 +54,14 @@ import WindowContainer from "./WindowContainer"
toRect(proto.animator?.lastClipRect ?? null), toRect(proto.animator?.lastClipRect ?? null),
windowContainer, windowContainer,
/* isAppWindow */ isActivityInTree /* isAppWindow */ isActivityInTree
) );
entry.kind = entry.constructor.name addAttributes(entry, proto);
entry.rect = entry.frame return entry;
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
} }
} }
function newWindowLayoutParams(proto): WindowLayoutParams { function createWindowLayoutParams(proto: any): WindowLayoutParams {
return new WindowLayoutParams( return new WindowLayoutParams(
/* type */ proto?.type ?? 0, /* type */ proto?.type ?? 0,
/* x */ proto?.x ?? 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 export default WindowState

View File

@@ -14,32 +14,29 @@
* limitations under the License. * limitations under the License.
*/ */
import { getPropertiesForDisplay, shortenName } from '../mixin' import { shortenName } from '../mixin'
import { asRawTreeViewObject } from '../../utils/diff.js'
import { WindowToken } from "../common" import { WindowToken } from "../common"
import WindowContainer from "./WindowContainer" import WindowContainer from "./WindowContainer"
WindowToken.fromProto = function (proto, isActivityInTree: Boolean): WindowToken { WindowToken.fromProto = function (proto: any, isActivityInTree: Boolean): WindowToken {
if (proto == null) { if (proto == null) {
return null return null;
} }
const children = proto.windowContainer.children.reverse() const windowContainer = WindowContainer.fromProto(
.filter(it => it != null) /* proto */ proto.windowContainer,
.map(it => WindowContainer.childrenFromProto(it, isActivityInTree)) /* protoChildren */ proto.windowContainer.children.reverse(),
const windowContainer = WindowContainer.fromProto({proto: proto.windowContainer, /* isActivityInTree */ isActivityInTree,
children: children, tokenOverride: proto.hashCode}) /* nameOverride */ null,
if (windowContainer == null) { /* identifierOverride */ null,
throw "Window container should not be null: " + JSON.stringify(proto) /* tokenOverride */ proto.hashCode
} );
const entry = new WindowToken(windowContainer) const entry = new WindowToken(windowContainer);
entry.kind = entry.constructor.name entry.kind = entry.constructor.name;
entry.obj = getPropertiesForDisplay(proto, entry) entry.proto = proto;
entry.shortName = shortenName(entry.name) entry.shortName = shortenName(entry.name);
entry.rawTreeViewObject = asRawTreeViewObject(entry) console.warn("Created ", entry.kind, " stableId=", entry.stableId);
return entry;
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) { updateTimelineTime(context, timestamp) {
for (const file of context.getters.files) { 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 type = file.type;
const entryIndex = findLastMatchingSorted( const entryIndex = findLastMatchingSorted(
file.timeline, file.timeline,

View File

@@ -14,198 +14,38 @@
* limitations under the License. * limitations under the License.
*/ */
/* transform type flags */ function multiplyVec2(matrix, x, y) {
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) {
if (!matrix) return {x, y}; if (!matrix) return {x, y};
// |dsdx dsdy tx| | x | // |dsdx dsdy tx| | x |
// |dtdx dtdy ty| x | y | // |dtdx dtdy ty| x | y |
// |0 0 1 | | 1 | // |0 0 1 | | 1 |
return { return {
x: matrix.dsdx * x + matrix.dsdy * y + matrix.tx, 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 | // |dsdx dsdy tx| | left, top |
// matrix = |dtdx dtdy ty| rect = | | // matrix = |dtdx dtdy ty| rect = | |
// |0 0 1 | | right, bottom | // |0 0 1 | | right, bottom |
var left_top = multiply_vec2(matrix, rect.left, rect.top); const leftTop = multiplyVec2(matrix, rect.left, rect.top);
var right_top = multiply_vec2(matrix, rect.right, rect.top); const rightTop = multiplyVec2(matrix, rect.right, rect.top);
var left_bottom = multiply_vec2(matrix, rect.left, rect.bottom); const leftBottom = multiplyVec2(matrix, rect.left, rect.bottom);
var right_bottom = multiply_vec2(matrix, rect.right, rect.bottom); const rightBottom = multiplyVec2(matrix, rect.right, rect.bottom);
var outrect = {}; const outrect = {};
outrect.left = Math.min(left_top.x, right_top.x, left_bottom.x, right_bottom.x); outrect.left = Math.min(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x);
outrect.top = Math.min(left_top.y, right_top.y, left_bottom.y, right_bottom.y); outrect.top = Math.min(leftTop.y, rightTop.y, leftBottom.y, rightBottom.y);
outrect.right = Math.max(left_top.x, right_top.x, left_bottom.x, right_bottom.x); outrect.right = Math.max(leftTop.x, rightTop.x, leftBottom.x, rightBottom.x);
outrect.bottom = Math.max(left_top.y, right_top.y, left_bottom.y, right_bottom.y); outrect.bottom = Math.max(leftTop.y, rightTop.y, leftBottom.y,
rightBottom.y);
return outrect; return outrect;
} }
// Returns true if the applying the transform on an an axis aligned rectangle export {multiplyRect};
// 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};

View File

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

View File

@@ -229,7 +229,14 @@ export default {
if (this.disabled) { if (this.disabled) {
return; 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); 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. * 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 { export default class Accessibility extends TraceBase {
obj: any 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 { FILE_TYPES, TRACE_TYPES } from '@/decode.js';
import TraceBase from './TraceBase'; import TraceBase from './TraceBase';
import { LayersTrace } from '@/flickerlib';
export default class SurfaceFlinger extends TraceBase { export default class SurfaceFlinger extends TraceBase {
sfTraceFile: any; sfTraceFile: Object;
constructor(files) { constructor(files) {
const sfTraceFile = files[FILE_TYPES.SURFACE_FLINGER_TRACE]; const sfTraceFile = files[FILE_TYPES.SURFACE_FLINGER_TRACE];
@@ -30,4 +31,8 @@ export default class SurfaceFlinger extends TraceBase {
get type() { get type() {
return TRACE_TYPES.SURFACE_FLINGER; 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; return TRACE_TYPES.WINDOW_MANAGER;
} }
static fromProto(proto): WindowManagerTrace { static fromProto(proto: any): WindowManagerTrace {
return WindowManagerTrace.fromProto(proto); return WindowManagerTrace.fromProto(proto);
} }
} }

View File

@@ -103,7 +103,7 @@ function transform({
stableId: stableIdResolved, stableId: stableIdResolved,
visible: call(visible, obj), visible: call(visible, obj),
childrenVisible: transformedChildren.some((c) => { childrenVisible: transformedChildren.some((c) => {
return c.childrenVisible || c.visible; return c.childrenVisible || c.isVisible;
}), }),
flattened: call(flattened, obj), flattened: call(flattened, obj),
}; };
@@ -309,7 +309,7 @@ class ObjectTransformer {
transformedObj = { transformedObj = {
kind: '', kind: '',
name: name + ': ' + child.name, name: (isTerminal(name) ? compareWithName : name) + ': ' + child.name,
stableId, stableId,
children: child.children, children: child.children,
combined: true, combined: true,
@@ -376,6 +376,24 @@ function nanos_to_string(elapsedRealtimeNanos) {
return parts.reverse().join(''); 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. // Returns a UI element used highlight a visible entry.
// eslint-disable-next-line camelcase // eslint-disable-next-line camelcase
function get_visible_chip() { function get_visible_chip() {
@@ -383,4 +401,4 @@ function get_visible_chip() {
} }
// eslint-disable-next-line camelcase // 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 // TODO (b/162300507): Get rid of cloning
import cloneDeep from 'lodash.clonedeep'; import ObjectFormatter from '../flickerlib/ObjectFormatter';
export const DiffType = Object.freeze({ export const DiffType = Object.freeze({
NONE: 'none', NONE: 'none',
@@ -26,21 +26,6 @@ export const DiffType = Object.freeze({
MODIFIED: 'modified', 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) { export function defaultModifiedCheck(newNode, oldNode) {
if (!newNode && !oldNode) { if (!newNode && !oldNode) {
return false; return false;
@@ -50,29 +35,16 @@ export function defaultModifiedCheck(newNode, oldNode) {
return true; return true;
} }
return JSON.stringify(newNode.obj) !== JSON.stringify(oldNode.obj); return !newNode.equals(oldNode);
}
function isPrimitive(test) {
return test !== Object(test);
} }
export class DiffGenerator { export class DiffGenerator {
constructor(tree) { constructor(tree) {
if (tree.rawTreeViewObject) { this.tree = tree;
this.tree = tree.rawTreeViewObject;
} else {
this.tree = tree;
}
} }
compareWith(tree) { compareWith(tree) {
if (tree?.rawTreeViewObject) { this.diffWithTree = tree;
this.diffWithTree = tree.rawTreeViewObject;
} else {
this.diffWithTree = tree;
}
return this; return this;
} }
@@ -140,16 +112,13 @@ export class DiffGenerator {
return Object.keys(obj).length === 0 && obj.constructor === Object; return Object.keys(obj).length === 0 && obj.constructor === Object;
} }
_cloneNodeWithoutChildren(node) { _cloneNode(node) {
const clone = {}; const clone = ObjectFormatter.cloneObject(node);
clone.children = node.children;
for (const key in node) { clone.name = node.name;
if (key !== 'children') { clone.kind = node.kind;
const val = node[key]; clone.stableId = node.stableId;
clone[key] = isPrimitive(val) ? val : cloneDeep(val); clone.shortName = node.shortName;
}
}
return clone; return clone;
} }
@@ -167,7 +136,7 @@ export class DiffGenerator {
// Deep clone newTree omitting children field // Deep clone newTree omitting children field
// Clone is required because trees are frozen objects — we can't // Clone is required because trees are frozen objects — we can't
// modify the original tree object. Also means there is no side effect. // 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 // Default to no changes
diffTree.diff = {type: DiffType.NONE}; diffTree.diff = {type: DiffType.NONE};
@@ -200,7 +169,7 @@ export class DiffGenerator {
// Check if oldTree has been deleted of moved // Check if oldTree has been deleted of moved
if (oldTree && !newTreeSiblingIds.includes(oldId)) { if (oldTree && !newTreeSiblingIds.includes(oldId)) {
const deletedTreeDiff = this._cloneNodeWithoutChildren(oldTree); const deletedTreeDiff = this._cloneNode(oldTree);
if (this.newMapping[oldId]) { if (this.newMapping[oldId]) {
deletedTreeDiff.diff = {type: DiffType.DELETED_MOVE}; deletedTreeDiff.diff = {type: DiffType.DELETED_MOVE};
@@ -233,7 +202,7 @@ export class DiffGenerator {
} else if (oldTree) { } else if (oldTree) {
if (!newTreeSiblingIds.includes(oldId)) { if (!newTreeSiblingIds.includes(oldId)) {
// Deep clone oldTree omitting children field // 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. // newTree doesn't exists, oldTree has either been moved or deleted.
if (this.newMapping[oldId]) { if (this.newMapping[oldId]) {
@@ -258,12 +227,12 @@ export class DiffGenerator {
// TODO: Try replacing this with some sort of zipWith. // TODO: Try replacing this with some sort of zipWith.
const numOfChildren = Math.max( 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++) { for (let i = 0; i < numOfChildren; i++) {
const newChild = i < newTree?.children.length ? const newChild = i < newTree?.children?.length ?
newTree.children[i] : null; newTree.children[i] : null;
const oldChild = i < oldTree?.children.length ? const oldChild = i < oldTree?.children?.length ?
oldTree.children[i] : null; oldTree.children[i] : null;
const childDiffTrees = this._generateDiffTree( const childDiffTrees = this._generateDiffTree(

View File

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

File diff suppressed because it is too large Load Diff