diff --git a/tools/winscope-ng/src/common/trace/screen_recording.ts b/tools/winscope-ng/src/common/trace/screen_recording.ts new file mode 100644 index 000000000..c9302c2a8 --- /dev/null +++ b/tools/winscope-ng/src/common/trace/screen_recording.ts @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +class ScreenRecordingTraceEntry { + constructor(public timestamp: number, + public videoTimeSeconds: number, + public videoData: Uint8Array) { + } +}; + +export {ScreenRecordingTraceEntry}; diff --git a/tools/winscope-ng/src/common/trace/type_id.ts b/tools/winscope-ng/src/common/trace/type_id.ts index 5c81b0dfe..e0c91ac53 100644 --- a/tools/winscope-ng/src/common/trace/type_id.ts +++ b/tools/winscope-ng/src/common/trace/type_id.ts @@ -17,8 +17,6 @@ enum TraceTypeId { ACCESSIBILITY, WINDOW_MANAGER, SURFACE_FLINGER, - WINDOW_MANAGER_DUMP, - SURFACE_FLINGER_DUMP, SCREEN_RECORDING, TRANSACTIONS, TRANSACTIONS_LEGACY, diff --git a/tools/winscope-ng/src/common/utils/array_utils.spec.ts b/tools/winscope-ng/src/common/utils/array_utils.spec.ts index aec14f65e..64d2d07ee 100644 --- a/tools/winscope-ng/src/common/utils/array_utils.spec.ts +++ b/tools/winscope-ng/src/common/utils/array_utils.spec.ts @@ -38,6 +38,24 @@ describe("ArrayUtils", () => { expect(ArrayUtils.equal(new Uint8Array([1, 2, 3]), new Uint8Array([1, 2, 3]))).toBeTrue(); }); + it("searchSubarray", () => { + expect(ArrayUtils.searchSubarray([], [0])).toEqual(undefined); + expect(ArrayUtils.searchSubarray([], [])).toEqual(0); + expect(ArrayUtils.searchSubarray([0], [])).toEqual(0); + + expect(ArrayUtils.searchSubarray([0, 1, 2], [-1])).toEqual(undefined); + expect(ArrayUtils.searchSubarray([0, 1, 2], [])).toEqual(0); + expect(ArrayUtils.searchSubarray([0, 1, 2], [0])).toEqual(0); + expect(ArrayUtils.searchSubarray([0, 1, 2], [1])).toEqual(1); + expect(ArrayUtils.searchSubarray([0, 1, 2], [2])).toEqual(2); + + expect(ArrayUtils.searchSubarray([0, 1, 2], [0, 1])).toEqual(0); + expect(ArrayUtils.searchSubarray([0, 1, 2], [1, 2])).toEqual(1); + expect(ArrayUtils.searchSubarray([0, 1, 2], [2])).toEqual(2); + expect(ArrayUtils.searchSubarray([0, 1, 2], [2, 3])).toEqual(undefined); + + }); + it("binarySearchLowerOrEqual", () => { // no match expect(ArrayUtils.binarySearchLowerOrEqual([], 5)).toBeUndefined(); @@ -59,4 +77,19 @@ describe("ArrayUtils", () => { expect(ArrayUtils.binarySearchLowerOrEqual([3, 4, 5, 6], 5)).toEqual(2); expect(ArrayUtils.binarySearchLowerOrEqual([3, 4, 5, 6, 7], 5)).toEqual(2); }); + + it("toUintLittleEndian", () => { + const buffer = new Uint8Array([0, 0, 1, 1]); + + expect(ArrayUtils.toUintLittleEndian(buffer, 0, -1)).toEqual(0); + expect(ArrayUtils.toUintLittleEndian(buffer, 0, 0)).toEqual(0); + + expect(ArrayUtils.toUintLittleEndian(buffer, 0, 1)).toEqual(0); + expect(ArrayUtils.toUintLittleEndian(buffer, 0, 2)).toEqual(0); + + expect(ArrayUtils.toUintLittleEndian(buffer, 3, 4)).toEqual(1); + expect(ArrayUtils.toUintLittleEndian(buffer, 2, 4)).toEqual(1 + 256); + expect(ArrayUtils.toUintLittleEndian(buffer, 1, 4)).toEqual(256 + 256*256); + expect(ArrayUtils.toUintLittleEndian(buffer, 0, 3)).toEqual(256*256); + }); }); diff --git a/tools/winscope-ng/src/common/utils/array_utils.ts b/tools/winscope-ng/src/common/utils/array_utils.ts index 0d29e3c5c..60bcc3223 100644 --- a/tools/winscope-ng/src/common/utils/array_utils.ts +++ b/tools/winscope-ng/src/common/utils/array_utils.ts @@ -39,6 +39,25 @@ class ArrayUtils { return true; } + static searchSubarray(array: T[] | TypedArray, subarray: T[] | TypedArray): number|undefined { + for (let i = 0; i + subarray.length <= array.length; ++i) { + let match = true; + + for (let j = 0; j < subarray.length; ++j) { + if (array[i + j] != subarray[j]) { + match = false; + break; + } + } + + if (match) { + return i; + } + } + + return undefined; + } + static binarySearchLowerOrEqual(values: T[] | TypedArray, target: T): number|undefined { if (values.length == 0) { return undefined; @@ -66,6 +85,15 @@ class ArrayUtils { return result; } + + static toUintLittleEndian(buffer: Uint8Array, start: number, end: number) { + let result = 0; + for (let i = end-1; i>=start; --i) { + result *= 256; + result += buffer[i]; + } + return result; + } } export {ArrayUtils}; diff --git a/tools/winscope-ng/src/parsers/parser.ts b/tools/winscope-ng/src/parsers/parser.ts index 2f8a60c12..7f35097eb 100644 --- a/tools/winscope-ng/src/parsers/parser.ts +++ b/tools/winscope-ng/src/parsers/parser.ts @@ -19,13 +19,16 @@ import {TraceTypeId} from "common/trace/type_id"; abstract class Parser { constructor(buffer: Uint8Array) { const magicNumber = this.getMagicNumber(); - const bufferContainsMagicNumber = ArrayUtils.equal(magicNumber, buffer.slice(0, magicNumber.length)); - if (!bufferContainsMagicNumber) { - throw TypeError("buffer doesn't contain expected magic number"); + if (magicNumber !== undefined) + { + const bufferContainsMagicNumber = ArrayUtils.equal(magicNumber, buffer.slice(0, magicNumber.length)); + if (!bufferContainsMagicNumber) { + throw TypeError("buffer doesn't contain expected magic number"); + } } - this.traceEntriesProto = this.decodeProto(buffer); - this.timestamps = this.traceEntriesProto.map((entryProto: any) => this.getTimestamp(entryProto)); + this.decodedEntries = this.decodeTrace(buffer); + this.timestamps = this.decodedEntries.map((entry: any) => this.getTimestamp(entry)); } public abstract getTraceTypeId(): TraceTypeId; @@ -34,20 +37,20 @@ abstract class Parser { return this.timestamps; } - public getTraceEntry(timestamp: number): any|undefined { + public getTraceEntry(timestamp: number): undefined|any { const index = ArrayUtils.binarySearchLowerOrEqual(this.getTimestamps(), timestamp); if (index === undefined) { return undefined; } - return this.processTraceEntryProto(this.traceEntriesProto[index]); + return this.processDecodedEntry(this.decodedEntries[index]); } - protected abstract getMagicNumber(): number[]; - protected abstract decodeProto(buffer: Uint8Array): any[]; - protected abstract getTimestamp(entryProto: any): number; - protected abstract processTraceEntryProto(entryProto: any): any; + protected abstract getMagicNumber(): undefined|number[]; + protected abstract decodeTrace(buffer: Uint8Array): any[]; + protected abstract getTimestamp(decodedEntry: any): number; + protected abstract processDecodedEntry(decodedEntry: any): any; - private traceEntriesProto: any[]; + private decodedEntries: any[]; private timestamps: number[]; } diff --git a/tools/winscope-ng/src/parsers/parser_accessibility.ts b/tools/winscope-ng/src/parsers/parser_accessibility.ts index 456f4f404..954a77ba4 100644 --- a/tools/winscope-ng/src/parsers/parser_accessibility.ts +++ b/tools/winscope-ng/src/parsers/parser_accessibility.ts @@ -30,7 +30,7 @@ class ParserAccessibility extends Parser { return ParserAccessibility.MAGIC_NUMBER; } - override decodeProto(buffer: Uint8Array): any[] { + override decodeTrace(buffer: Uint8Array): any[] { return (AccessibilityTraceFileProto.decode(buffer)).entry; } @@ -38,7 +38,7 @@ class ParserAccessibility extends Parser { return Number(entryProto.elapsedRealtimeNanos); } - override processTraceEntryProto(entryProto: any): any { + override processDecodedEntry(entryProto: any): any { return entryProto; } diff --git a/tools/winscope-ng/src/parsers/parser_factory.ts b/tools/winscope-ng/src/parsers/parser_factory.ts index e4ae8bfe1..2546325fc 100644 --- a/tools/winscope-ng/src/parsers/parser_factory.ts +++ b/tools/winscope-ng/src/parsers/parser_factory.ts @@ -19,9 +19,11 @@ import {ParserInputMethodClients} from "./parser_input_method_clients"; import {ParserInputMethodManagerService} from "./parser_input_method_manager_service"; import {ParserInputMethodService} from "./parser_input_method_service"; import {ParserProtoLog} from "./parser_protolog" +import {ParserScreenRecording} from "./parser_screen_recording" import {ParserSurfaceFlinger} from "./parser_surface_flinger" import {ParserTransactions} from "./parser_transactions"; import {ParserWindowManager} from "./parser_window_manager" +import {ParserWindowManagerDump} from "./parser_window_manager_dump" class ParserFactory { static readonly PARSERS = [ @@ -30,9 +32,11 @@ class ParserFactory { ParserInputMethodManagerService, ParserInputMethodService, ParserProtoLog, + ParserScreenRecording, ParserSurfaceFlinger, ParserTransactions, ParserWindowManager, + ParserWindowManagerDump, ] createParsers(buffers: Uint8Array[]): Parser[] { diff --git a/tools/winscope-ng/src/parsers/parser_input_method_clients.ts b/tools/winscope-ng/src/parsers/parser_input_method_clients.ts index f4fdcc86b..31a14190a 100644 --- a/tools/winscope-ng/src/parsers/parser_input_method_clients.ts +++ b/tools/winscope-ng/src/parsers/parser_input_method_clients.ts @@ -30,7 +30,7 @@ class ParserInputMethodClients extends Parser { return ParserInputMethodClients.MAGIC_NUMBER; } - override decodeProto(buffer: Uint8Array): any[] { + override decodeTrace(buffer: Uint8Array): any[] { return (InputMethodClientsTraceFileProto.decode(buffer)).entry; } @@ -38,7 +38,7 @@ class ParserInputMethodClients extends Parser { return Number(entryProto.elapsedRealtimeNanos); } - override processTraceEntryProto(entryProto: any): any { + override processDecodedEntry(entryProto: any): any { return entryProto; } diff --git a/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts b/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts index 352f4352c..772d1678d 100644 --- a/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts +++ b/tools/winscope-ng/src/parsers/parser_input_method_manager_service.ts @@ -30,7 +30,7 @@ class ParserInputMethodManagerService extends Parser { return ParserInputMethodManagerService.MAGIC_NUMBER; } - override decodeProto(buffer: Uint8Array): any[] { + override decodeTrace(buffer: Uint8Array): any[] { return (InputMethodManagerServiceTraceFileProto.decode(buffer)).entry; } @@ -38,7 +38,7 @@ class ParserInputMethodManagerService extends Parser { return Number(entryProto.elapsedRealtimeNanos); } - protected override processTraceEntryProto(entryProto: any): any { + protected override processDecodedEntry(entryProto: any): any { return entryProto; } diff --git a/tools/winscope-ng/src/parsers/parser_input_method_service.ts b/tools/winscope-ng/src/parsers/parser_input_method_service.ts index 4a1f3e8b8..b13109dfc 100644 --- a/tools/winscope-ng/src/parsers/parser_input_method_service.ts +++ b/tools/winscope-ng/src/parsers/parser_input_method_service.ts @@ -30,7 +30,7 @@ class ParserInputMethodService extends Parser { return ParserInputMethodService.MAGIC_NUMBER; } - override decodeProto(buffer: Uint8Array): any[] { + override decodeTrace(buffer: Uint8Array): any[] { return (InputMethodServiceTraceFileProto.decode(buffer)).entry; } @@ -38,7 +38,7 @@ class ParserInputMethodService extends Parser { return Number(entryProto.elapsedRealtimeNanos); } - override processTraceEntryProto(entryProto: any): any { + override processDecodedEntry(entryProto: any): any { return entryProto; } diff --git a/tools/winscope-ng/src/parsers/parser_protolog.ts b/tools/winscope-ng/src/parsers/parser_protolog.ts index 344f3d859..13cc51881 100644 --- a/tools/winscope-ng/src/parsers/parser_protolog.ts +++ b/tools/winscope-ng/src/parsers/parser_protolog.ts @@ -32,7 +32,7 @@ class ParserProtoLog extends Parser { return ParserProtoLog.MAGIC_NUMBER; } - override decodeProto(buffer: Uint8Array): any[] { + override decodeTrace(buffer: Uint8Array): any[] { const fileProto: any = ProtoLogFileProto.decode(buffer); if (fileProto.version !== ParserProtoLog.PROTOLOG_VERSION) { @@ -58,7 +58,7 @@ class ParserProtoLog extends Parser { return Number(entryProto.elapsedRealtimeNanos); } - override processTraceEntryProto(entryProto: any): LogMessage { + override processDecodedEntry(entryProto: any): LogMessage { const message = (configJson).messages[entryProto.messageHash]; if (!message) { return new FormattedLogMessage(entryProto); diff --git a/tools/winscope-ng/src/parsers/parser_screen_recording.spec.ts b/tools/winscope-ng/src/parsers/parser_screen_recording.spec.ts new file mode 100644 index 000000000..a8be68bc7 --- /dev/null +++ b/tools/winscope-ng/src/parsers/parser_screen_recording.spec.ts @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {ScreenRecordingTraceEntry} from "common/trace/screen_recording"; +import {TraceTypeId} from "common/trace/type_id"; +import {TestUtils} from 'test/test_utils'; +import {Parser} from './parser'; +import {ParserFactory} from './parser_factory'; + +describe("ParserScreenRecording", () => { + let parser: Parser; + + beforeAll(() => { + const buffer = TestUtils.loadFixture("screen_recording.mp4"); + const parsers = new ParserFactory().createParsers([buffer]); + expect(parsers.length).toEqual(1); + parser = parsers[0]; + }); + + it("has expected trace type", () => { + expect(parser.getTraceTypeId()).toEqual(TraceTypeId.SCREEN_RECORDING); + }); + + it("provides timestamps", () => { + const timestamps = parser.getTimestamps(); + + expect(timestamps.length) + .toEqual(85); + + expect(timestamps.slice(0, 3)) + .toEqual([19446131807000, 19446158500000, 19446167117000]); + + expect(timestamps.slice(timestamps.length-3, timestamps.length)) + .toEqual([19448470076000, 19448487525000, 19448501007000]); + }); + + it("retrieves trace entry", () => { + { + const entry = parser.getTraceEntry(19446131807000)! + expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry); + expect(Number(entry.videoTimeSeconds)).toBeCloseTo(0); + } + + { + const entry = parser.getTraceEntry(19448501007000)! + expect(entry).toBeInstanceOf(ScreenRecordingTraceEntry); + expect(Number(entry.videoTimeSeconds)).toBeCloseTo(2.37, 0.001); + } + }); +}); diff --git a/tools/winscope-ng/src/parsers/parser_screen_recording.ts b/tools/winscope-ng/src/parsers/parser_screen_recording.ts new file mode 100644 index 000000000..28aa6b80a --- /dev/null +++ b/tools/winscope-ng/src/parsers/parser_screen_recording.ts @@ -0,0 +1,85 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {TraceTypeId} from "common/trace/type_id"; +import {ArrayUtils} from "common/utils/array_utils"; +import {Parser} from "./parser" +import {ScreenRecordingTraceEntry} from "common/trace/screen_recording"; + +class ParserScreenRecording extends Parser { + constructor(private videoData: Uint8Array) { + super(videoData); + } + + override getTraceTypeId(): TraceTypeId { + return TraceTypeId.SCREEN_RECORDING; + } + + override getMagicNumber(): number[] { + return ParserScreenRecording.MPEG4_MAGIC_NMBER; + } + + override decodeTrace(videoData: Uint8Array): number[] { + let posCount = this.searchMagicString(videoData); + let [posTimestamps, count] = this.parseTimestampsCount(videoData, posCount); + return this.parseTimestamps(videoData, posTimestamps, count); + } + + override getTimestamp(decodedEntry: number): number { + return decodedEntry; + } + + override processDecodedEntry(timestamp: number): ScreenRecordingTraceEntry { + const videoTimeSeconds = (timestamp - this.timestamps[0]) / 1000000000 + ParserScreenRecording.EPSILON; + return new ScreenRecordingTraceEntry(timestamp, videoTimeSeconds, this.videoData); + } + + private searchMagicString(videoData: Uint8Array): number { + let pos = ArrayUtils.searchSubarray(videoData, ParserScreenRecording.WINSCOPE_META_MAGIC_STRING); + if (pos === undefined) { + throw new TypeError("video data doesn't contain winscope magic string"); + } + pos += ParserScreenRecording.WINSCOPE_META_MAGIC_STRING.length; + return pos; + } + + private parseTimestampsCount(videoData: Uint8Array, pos: number) : [number, number] { + if (pos + 4 >= videoData.length) { + throw new TypeError("video data is too short. Expected timestamps count doesn't fit"); + } + const timestampsCount = ArrayUtils.toUintLittleEndian(videoData, pos, pos+4); + pos += 4; + return [pos, timestampsCount]; + } + + private parseTimestamps(videoData: Uint8Array, pos: number, count: number): number[] { + if (pos + count * 8 >= videoData.length) { + throw new TypeError("video data is too short. Expected timestamps do not fit"); + } + const timestamps: number[] = []; + for (let i = 0; i < count; ++i) { + const timestamp = ArrayUtils.toUintLittleEndian(videoData, pos, pos+8) * 1000; + pos += 8; + timestamps.push(timestamp); + } + return timestamps; + } + + private static readonly MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42 + private static readonly WINSCOPE_META_MAGIC_STRING = [0x23, 0x56, 0x56, 0x31, 0x4e, 0x53, 0x43, 0x30, 0x50, 0x45, 0x54, 0x31, 0x4d, 0x45, 0x21, 0x23]; // #VV1NSC0PET1ME!# + private static readonly EPSILON = 0.00001; +} + +export {ParserScreenRecording}; diff --git a/tools/winscope-ng/src/parsers/parser_surface_flinger.ts b/tools/winscope-ng/src/parsers/parser_surface_flinger.ts index e7b88ee91..ec96644c6 100644 --- a/tools/winscope-ng/src/parsers/parser_surface_flinger.ts +++ b/tools/winscope-ng/src/parsers/parser_surface_flinger.ts @@ -31,7 +31,7 @@ class ParserSurfaceFlinger extends Parser { return ParserSurfaceFlinger.MAGIC_NUMBER; } - override decodeProto(buffer: Uint8Array): any[] { + override decodeTrace(buffer: Uint8Array): any[] { return (LayersTraceFileProto.decode(buffer)).entry; } @@ -39,7 +39,7 @@ class ParserSurfaceFlinger extends Parser { return Number(entryProto.elapsedRealtimeNanos); } - override processTraceEntryProto(entryProto: any): any { + override processDecodedEntry(entryProto: any): any { return LayerTraceEntry.fromProto(entryProto.layers.layers, entryProto.displays, entryProto.elapsedRealtimeNanos, entryProto.hwcBlob); } diff --git a/tools/winscope-ng/src/parsers/parser_surface_flinger_dump.spec.ts b/tools/winscope-ng/src/parsers/parser_surface_flinger_dump.spec.ts new file mode 100644 index 000000000..29b17af4d --- /dev/null +++ b/tools/winscope-ng/src/parsers/parser_surface_flinger_dump.spec.ts @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {TraceTypeId} from "common/trace/type_id"; +import {LayerTraceEntry} from 'common/trace/flickerlib/layers/LayerTraceEntry'; +import {TestUtils} from 'test/test_utils'; +import {Parser} from './parser'; +import {ParserFactory} from './parser_factory'; + +describe("ParserSurfaceFlingerDump", () => { + let parser: Parser; + + beforeAll(() => { + const buffer = TestUtils.loadFixture("dump_SurfaceFlinger.pb"); + const parsers = new ParserFactory().createParsers([buffer]); + expect(parsers.length).toEqual(1); + parser = parsers[0]; + }); + + it("has expected trace type", () => { + expect(parser.getTraceTypeId()).toEqual(TraceTypeId.SURFACE_FLINGER); + }); + + it("provides timestamp", () => { + expect(parser.getTimestamps()).toEqual([0]); + }); + + it("retrieves trace entry", () => { + const entry = parser.getTraceEntry(0)! + expect(entry).toBeInstanceOf(LayerTraceEntry); + expect(Number(entry.timestampMs)).toEqual(0); + }); +}); diff --git a/tools/winscope-ng/src/parsers/parser_transactions.ts b/tools/winscope-ng/src/parsers/parser_transactions.ts index f9091cbb1..4f4c0e089 100644 --- a/tools/winscope-ng/src/parsers/parser_transactions.ts +++ b/tools/winscope-ng/src/parsers/parser_transactions.ts @@ -30,7 +30,7 @@ class ParserTransactions extends Parser { return ParserTransactions.MAGIC_NUMBER; } - override decodeProto(buffer: Uint8Array): any[] { + override decodeTrace(buffer: Uint8Array): any[] { return (TransactionsTraceFileProto.decode(buffer)).entry; } @@ -38,7 +38,7 @@ class ParserTransactions extends Parser { return Number(entryProto.elapsedRealtimeNanos); } - override processTraceEntryProto(entryProto: any): any { + override processDecodedEntry(entryProto: any): any { return entryProto; } diff --git a/tools/winscope-ng/src/parsers/parser_window_manager.ts b/tools/winscope-ng/src/parsers/parser_window_manager.ts index 00320cf36..122157f76 100644 --- a/tools/winscope-ng/src/parsers/parser_window_manager.ts +++ b/tools/winscope-ng/src/parsers/parser_window_manager.ts @@ -31,7 +31,7 @@ class ParserWindowManager extends Parser { return ParserWindowManager.MAGIC_NUMBER; } - override decodeProto(buffer: Uint8Array): any { + override decodeTrace(buffer: Uint8Array): any[] { return (WindowManagerTraceFileProto.decode(buffer)).entry; } @@ -39,7 +39,7 @@ class ParserWindowManager extends Parser { return Number(entryProto.elapsedRealtimeNanos); } - override processTraceEntryProto(entryProto: any): WindowManagerState { + override processDecodedEntry(entryProto: any): WindowManagerState { return WindowManagerState.fromProto(entryProto.windowManagerService, entryProto.elapsedRealtimeNanos, entryProto.where); } diff --git a/tools/winscope-ng/src/parsers/parser_window_manager_dump.spec.ts b/tools/winscope-ng/src/parsers/parser_window_manager_dump.spec.ts new file mode 100644 index 000000000..b97c69608 --- /dev/null +++ b/tools/winscope-ng/src/parsers/parser_window_manager_dump.spec.ts @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {WindowManagerState} from 'common/trace/flickerlib/windows/WindowManagerState'; +import {TraceTypeId} from "common/trace/type_id"; +import {ParserFactory} from "./parser_factory"; +import {Parser} from "./parser"; +import {TestUtils} from "test/test_utils"; + +describe("ParserWindowManagerDump", () => { + let parser: Parser; + + beforeAll(() => { + const buffer = TestUtils.loadFixture("dump_WindowManager.pb"); + const parsers = new ParserFactory().createParsers([buffer]); + expect(parsers.length).toEqual(1); + parser = parsers[0]; + }); + + it("has expected trace type", () => { + expect(parser.getTraceTypeId()).toEqual(TraceTypeId.WINDOW_MANAGER); + }); + + it("provides timestamps", () => { + expect(parser.getTimestamps()) + .toEqual([0]); + }); + + it("retrieves trace entry", () => { + const entry = parser.getTraceEntry(0)!; + expect(entry).toBeInstanceOf(WindowManagerState); + expect(Number(entry.timestampMs)).toEqual(0); + }); +}); diff --git a/tools/winscope-ng/src/parsers/parser_window_manager_dump.ts b/tools/winscope-ng/src/parsers/parser_window_manager_dump.ts new file mode 100644 index 000000000..c8874ea61 --- /dev/null +++ b/tools/winscope-ng/src/parsers/parser_window_manager_dump.ts @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import {TraceTypeId} from "common/trace/type_id"; +import {Parser} from './parser' +import {WindowManagerServiceDumpProto} from './proto_types'; +import {WindowManagerState} from 'common/trace/flickerlib/windows/WindowManagerState'; + +class ParserWindowManagerDump extends Parser { + constructor(buffer: Uint8Array) { + super(buffer); + } + + override getTraceTypeId(): TraceTypeId { + return TraceTypeId.WINDOW_MANAGER; + } + + override getMagicNumber(): undefined { + return undefined; + } + + override decodeTrace(buffer: Uint8Array): any[] { + return [WindowManagerServiceDumpProto.decode(buffer)]; + } + + override getTimestamp(entryProto: any): number { + return 0; + } + + override processDecodedEntry(entryProto: any): WindowManagerState { + return WindowManagerState.fromProto(entryProto); + } +} + +export {ParserWindowManagerDump}; diff --git a/tools/winscope-ng/src/parsers/proto_types.js b/tools/winscope-ng/src/parsers/proto_types.js index 3c04347a8..964794335 100644 --- a/tools/winscope-ng/src/parsers/proto_types.js +++ b/tools/winscope-ng/src/parsers/proto_types.js @@ -14,6 +14,7 @@ const InputMethodServiceTraceFileProto = protobuf.Root.fromJSON(inputMethodClien const LayersTraceFileProto = protobuf.Root.fromJSON(layersJson).lookupType("android.surfaceflinger.LayersTraceFileProto"); const ProtoLogFileProto = protobuf.Root.fromJSON(protoLogJson).lookupType("com.android.internal.protolog.ProtoLogFileProto"); const TransactionsTraceFileProto = protobuf.Root.fromJSON(transactionsJson).lookupType("android.surfaceflinger.proto.TransactionTraceFile"); +const WindowManagerServiceDumpProto = protobuf.Root.fromJSON(windowManagerJson).lookupType("com.android.server.wm.WindowManagerServiceDumpProto"); const WindowManagerTraceFileProto = protobuf.Root.fromJSON(windowManagerJson).lookupType("com.android.server.wm.WindowManagerTraceFileProto"); export { @@ -24,5 +25,6 @@ export { LayersTraceFileProto, ProtoLogFileProto, TransactionsTraceFileProto, + WindowManagerServiceDumpProto, WindowManagerTraceFileProto }; diff --git a/tools/winscope-ng/src/test/fixtures/dump_SurfaceFlinger.pb b/tools/winscope-ng/src/test/fixtures/dump_SurfaceFlinger.pb new file mode 100644 index 000000000..46ee306a1 Binary files /dev/null and b/tools/winscope-ng/src/test/fixtures/dump_SurfaceFlinger.pb differ diff --git a/tools/winscope-ng/src/test/fixtures/dump_WindowManager.pb b/tools/winscope-ng/src/test/fixtures/dump_WindowManager.pb new file mode 100644 index 000000000..ab7fe9fbd Binary files /dev/null and b/tools/winscope-ng/src/test/fixtures/dump_WindowManager.pb differ diff --git a/tools/winscope-ng/src/test/fixtures/screen_recording.mp4 b/tools/winscope-ng/src/test/fixtures/screen_recording.mp4 new file mode 100644 index 000000000..9cdedfebe Binary files /dev/null and b/tools/winscope-ng/src/test/fixtures/screen_recording.mp4 differ