• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright 2017, The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17/* eslint-disable camelcase */
18/* eslint-disable max-len */
19
20import jsonProtoDefsWm from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto';
21import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/internal/protolog.proto';
22import jsonProtoDefsSf from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto';
23import jsonProtoDefsTransaction from 'frameworks/native/cmds/surfacereplayer/proto/src/trace.proto';
24import jsonProtoDefsWl from 'WaylandSafePath/waylandtrace.proto';
25import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto';
26import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto';
27import jsonProtoDefsIme from 'frameworks/base/core/proto/android/view/inputmethod/inputmethodeditortrace.proto';
28import protobuf from 'protobufjs';
29import {transformLayers, transformLayersTrace} from './transform_sf.js';
30import {transform_transaction_trace} from './transform_transaction.js';
31import {transform_wl_outputstate, transform_wayland_trace} from './transform_wl.js';
32import {transformProtolog} from './transform_protolog.js';
33import {transform_sysui_trace} from './transform_sys_ui.js';
34import {transform_launcher_trace} from './transform_launcher.js';
35import {transform_ime_trace_clients, transform_ime_trace_service, transform_ime_trace_managerservice} from './transform_ime.js';
36import {fill_transform_data} from './matrix_utils.js';
37import {mp4Decoder} from './decodeVideo.js';
38
39import SurfaceFlingerTrace from '@/traces/SurfaceFlinger.ts';
40import WindowManagerTrace from '@/traces/WindowManager.ts';
41import TransactionsTrace from '@/traces/Transactions.ts';
42import ScreenRecordingTrace from '@/traces/ScreenRecording.ts';
43import WaylandTrace from '@/traces/Wayland.ts';
44import ProtoLogTrace from '@/traces/ProtoLog.ts';
45import SystemUITrace from '@/traces/SystemUI.ts';
46import LauncherTrace from '@/traces/Launcher.ts';
47import ImeTraceClients from '@/traces/InputMethodClients.ts';
48import ImeTraceService from '@/traces/InputMethodService.ts';
49import ImeTraceManagerService from '@/traces/InputMethodManagerService.ts';
50
51import SurfaceFlingerDump from '@/dumps/SurfaceFlinger.ts';
52import WindowManagerDump from '@/dumps/WindowManager.ts';
53import WaylandDump from '@/dumps/Wayland.ts';
54
55const WmTraceMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerTraceFileProto');
56const WmDumpMessage = lookup_type(jsonProtoDefsWm, 'com.android.server.wm.WindowManagerServiceDumpProto');
57const SfTraceMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersTraceFileProto');
58const SfDumpMessage = lookup_type(jsonProtoDefsSf, 'android.surfaceflinger.LayersProto');
59const SfTransactionTraceMessage = lookup_type(jsonProtoDefsTransaction, 'Trace');
60const WaylandTraceMessage = lookup_type(jsonProtoDefsWl, 'org.chromium.arc.wayland_composer.TraceFileProto');
61const WaylandDumpMessage = lookup_type(jsonProtoDefsWl, 'org.chromium.arc.wayland_composer.OutputStateProto');
62const ProtoLogMessage = lookup_type(jsonProtoDefsProtoLog, 'com.android.internal.protolog.ProtoLogFileProto');
63const SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, 'com.android.systemui.tracing.SystemUiTraceFileProto');
64const LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, 'com.android.launcher3.tracing.LauncherTraceFileProto');
65const InputMethodClientsTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodClientsTraceFileProto");
66const InputMethodServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodServiceTraceFileProto");
67const InputMethodManagerServiceTraceMessage = lookup_type(jsonProtoDefsIme, "android.view.inputmethod.InputMethodManagerServiceTraceFileProto");
68
69const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45]; // .LYRTRACE
70const WINDOW_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WINTRACE
71const MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32]; // ....ftypmp42
72const WAYLAND_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x59, 0x4c, 0x54, 0x52, 0x41, 0x43, 0x45]; // .WYLTRACE
73const PROTO_LOG_MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47]; // .PROTOLOG
74const SYSTEM_UI_MAGIC_NUMBER = [0x09, 0x53, 0x59, 0x53, 0x55, 0x49, 0x54, 0x52, 0x43]; // .SYSUITRC
75const LAUNCHER_MAGIC_NUMBER = [0x09, 0x4C, 0x4E, 0x43, 0x48, 0x52, 0x54, 0x52, 0x43]; // .LNCHRTRC
76const IMC_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x43, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMCTRACE
77const IMS_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x53, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMSTRACE
78const IMM_TRACE_MAGIC_NUMBER = [0x09, 0x49, 0x4d, 0x4d, 0x54, 0x52, 0x41, 0x43, 0x45] //.IMMTRACE
79
80const FILE_TYPES = Object.freeze({
81  WINDOW_MANAGER_TRACE: 'WindowManagerTrace',
82  SURFACE_FLINGER_TRACE: 'SurfaceFlingerTrace',
83  WINDOW_MANAGER_DUMP: 'WindowManagerDump',
84  SURFACE_FLINGER_DUMP: 'SurfaceFlingerDump',
85  SCREEN_RECORDING: 'ScreenRecording',
86  TRANSACTIONS_TRACE: 'TransactionsTrace',
87  WAYLAND_TRACE: 'WaylandTrace',
88  WAYLAND_DUMP: 'WaylandDump',
89  PROTO_LOG: 'ProtoLog',
90  SYSTEM_UI: 'SystemUI',
91  LAUNCHER: 'Launcher',
92  IME_TRACE_CLIENTS: 'ImeTraceClients',
93  IME_TRACE_SERVICE: 'ImeTrace InputMethodService',
94  IME_TRACE_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
95});
96
97const WINDOW_MANAGER_ICON = 'view_compact';
98const SURFACE_FLINGER_ICON = 'filter_none';
99const SCREEN_RECORDING_ICON = 'videocam';
100const TRANSACTION_ICON = 'timeline';
101const WAYLAND_ICON = 'filter_none';
102const PROTO_LOG_ICON = 'notes';
103const SYSTEM_UI_ICON = 'filter_none';
104const LAUNCHER_ICON = 'filter_none';
105const IME_ICON = 'keyboard';
106
107const FILE_ICONS = {
108  [FILE_TYPES.WINDOW_MANAGER_TRACE]: WINDOW_MANAGER_ICON,
109  [FILE_TYPES.SURFACE_FLINGER_TRACE]: SURFACE_FLINGER_ICON,
110  [FILE_TYPES.WINDOW_MANAGER_DUMP]: WINDOW_MANAGER_ICON,
111  [FILE_TYPES.SURFACE_FLINGER_DUMP]: SURFACE_FLINGER_ICON,
112  [FILE_TYPES.SCREEN_RECORDING]: SCREEN_RECORDING_ICON,
113  [FILE_TYPES.TRANSACTIONS_TRACE]: TRANSACTION_ICON,
114  [FILE_TYPES.WAYLAND_TRACE]: WAYLAND_ICON,
115  [FILE_TYPES.WAYLAND_DUMP]: WAYLAND_ICON,
116  [FILE_TYPES.PROTO_LOG]: PROTO_LOG_ICON,
117  [FILE_TYPES.SYSTEM_UI]: SYSTEM_UI_ICON,
118  [FILE_TYPES.LAUNCHER]: LAUNCHER_ICON,
119  [FILE_TYPES.IME_TRACE_CLIENTS]: IME_ICON,
120  [FILE_TYPES.IME_TRACE_SERVICE]: IME_ICON,
121  [FILE_TYPES.IME_TRACE_MANAGERSERVICE]: IME_ICON,
122};
123
124function oneOf(dataType) {
125  return {oneOf: true, type: dataType};
126}
127
128function manyOf(dataType, fold = null) {
129  return {manyOf: true, type: dataType, fold};
130}
131
132const TRACE_TYPES = Object.freeze({
133  WINDOW_MANAGER: 'WindowManagerTrace',
134  SURFACE_FLINGER: 'SurfaceFlingerTrace',
135  SCREEN_RECORDING: 'ScreenRecording',
136  TRANSACTION: 'Transaction',
137  WAYLAND: 'Wayland',
138  PROTO_LOG: 'ProtoLog',
139  SYSTEM_UI: 'SystemUI',
140  LAUNCHER: 'Launcher',
141  IME_CLIENTS: 'ImeTrace Clients',
142  IME_SERVICE: 'ImeTrace InputMethodService',
143  IME_MANAGERSERVICE: 'ImeTrace InputMethodManagerService',
144});
145
146const TRACE_INFO = {
147  [TRACE_TYPES.WINDOW_MANAGER]: {
148    name: 'WindowManager',
149    icon: WINDOW_MANAGER_ICON,
150    files: [oneOf(FILE_TYPES.WINDOW_MANAGER_TRACE)],
151    constructor: WindowManagerTrace,
152  },
153  [TRACE_TYPES.SURFACE_FLINGER]: {
154    name: 'SurfaceFlinger',
155    icon: SURFACE_FLINGER_ICON,
156    files: [oneOf(FILE_TYPES.SURFACE_FLINGER_TRACE)],
157    constructor: SurfaceFlingerTrace,
158  },
159  [TRACE_TYPES.SCREEN_RECORDING]: {
160    name: 'Screen recording',
161    icon: SCREEN_RECORDING_ICON,
162    files: [oneOf(FILE_TYPES.SCREEN_RECORDING)],
163    constructor: ScreenRecordingTrace,
164  },
165  [TRACE_TYPES.TRANSACTION]: {
166    name: 'Transaction',
167    icon: TRANSACTION_ICON,
168    files: [
169      oneOf(FILE_TYPES.TRANSACTIONS_TRACE),
170    ],
171    constructor: TransactionsTrace,
172  },
173  [TRACE_TYPES.WAYLAND]: {
174    name: 'Wayland',
175    icon: WAYLAND_ICON,
176    files: [oneOf(FILE_TYPES.WAYLAND_TRACE)],
177    constructor: WaylandTrace,
178  },
179  [TRACE_TYPES.PROTO_LOG]: {
180    name: 'ProtoLog',
181    icon: PROTO_LOG_ICON,
182    files: [oneOf(FILE_TYPES.PROTO_LOG)],
183    constructor: ProtoLogTrace,
184  },
185  [TRACE_TYPES.SYSTEM_UI]: {
186    name: 'SystemUI',
187    icon: SYSTEM_UI_ICON,
188    files: [oneOf(FILE_TYPES.SYSTEM_UI)],
189    constructor: SystemUITrace,
190  },
191  [TRACE_TYPES.LAUNCHER]: {
192    name: 'Launcher',
193    icon: LAUNCHER_ICON,
194    files: [oneOf(FILE_TYPES.LAUNCHER)],
195    constructor: LauncherTrace,
196  },
197  [TRACE_TYPES.IME_CLIENTS]: {
198    name: 'InputMethodClients',
199    icon: IME_ICON,
200    files: [oneOf(FILE_TYPES.IME_TRACE_CLIENTS)],
201    constructor: ImeTraceClients,
202  },
203  [TRACE_TYPES.IME_SERVICE]: {
204    name: 'InputMethodService',
205    icon: IME_ICON,
206    files: [oneOf(FILE_TYPES.IME_TRACE_SERVICE)],
207    constructor: ImeTraceService,
208  },
209  [TRACE_TYPES.IME_MANAGERSERVICE]: {
210    name: 'InputMethodManagerService',
211    icon: IME_ICON,
212    files: [oneOf(FILE_TYPES.IME_TRACE_MANAGERSERVICE)],
213    constructor: ImeTraceManagerService,
214  },
215};
216
217const DUMP_TYPES = Object.freeze({
218  WINDOW_MANAGER: 'WindowManagerDump',
219  SURFACE_FLINGER: 'SurfaceFlingerDump',
220  WAYLAND: 'WaylandDump',
221});
222
223const DUMP_INFO = {
224  [DUMP_TYPES.WINDOW_MANAGER]: {
225    name: 'WindowManager',
226    icon: WINDOW_MANAGER_ICON,
227    files: [oneOf(FILE_TYPES.WINDOW_MANAGER_DUMP)],
228    constructor: WindowManagerDump,
229  },
230  [DUMP_TYPES.SURFACE_FLINGER]: {
231    name: 'SurfaceFlinger',
232    icon: SURFACE_FLINGER_ICON,
233    files: [oneOf(FILE_TYPES.SURFACE_FLINGER_DUMP)],
234    constructor: SurfaceFlingerDump,
235  },
236  [DUMP_TYPES.WAYLAND]: {
237    name: 'Wayland',
238    icon: WAYLAND_ICON,
239    files: [oneOf(FILE_TYPES.WAYLAND_DUMP)],
240    constructor: WaylandDump,
241  },
242};
243
244export const TRACE_ICONS = {
245  [TRACE_TYPES.WINDOW_MANAGER]: WINDOW_MANAGER_ICON,
246  [TRACE_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON,
247  [TRACE_TYPES.SCREEN_RECORDING]: SCREEN_RECORDING_ICON,
248  [TRACE_TYPES.TRANSACTION]: TRANSACTION_ICON,
249  [TRACE_TYPES.WAYLAND]: WAYLAND_ICON,
250  [TRACE_TYPES.PROTO_LOG]: PROTO_LOG_ICON,
251  [TRACE_TYPES.SYSTEM_UI]: SYSTEM_UI_ICON,
252  [TRACE_TYPES.LAUNCHER]: LAUNCHER_ICON,
253  [TRACE_TYPES.IME_CLIENTS]: IME_ICON,
254  [TRACE_TYPES.IME_SERVICE]: IME_ICON,
255  [TRACE_TYPES.IME_MANAGERSERVICE]: IME_ICON,
256
257  [DUMP_TYPES.WINDOW_MANAGER]: WINDOW_MANAGER_ICON,
258  [DUMP_TYPES.SURFACE_FLINGER]: SURFACE_FLINGER_ICON,
259  [DUMP_TYPES.WAYLAND]: WAYLAND_ICON,
260};
261
262// TODO: Rename name to defaultName
263const FILE_DECODERS = {
264  [FILE_TYPES.WINDOW_MANAGER_TRACE]: {
265    name: 'WindowManager trace',
266    decoder: protoDecoder,
267    decoderParams: {
268      type: FILE_TYPES.WINDOW_MANAGER_TRACE,
269      protoType: WmTraceMessage,
270      transform: WindowManagerTrace.fromProto,
271      timeline: true,
272    },
273  },
274  [FILE_TYPES.SURFACE_FLINGER_TRACE]: {
275    name: 'SurfaceFlinger trace',
276    decoder: protoDecoder,
277    decoderParams: {
278      type: FILE_TYPES.SURFACE_FLINGER_TRACE,
279      mime: 'application/octet-stream',
280      protoType: SfTraceMessage,
281      transform: transformLayersTrace,
282      timeline: true,
283    },
284  },
285  [FILE_TYPES.WAYLAND_TRACE]: {
286    name: 'Wayland trace',
287    decoder: protoDecoder,
288    decoderParams: {
289      type: FILE_TYPES.WAYLAND_TRACE,
290      mime: 'application/octet-stream',
291      protoType: WaylandTraceMessage,
292      transform: transform_wayland_trace,
293      timeline: true,
294    },
295  },
296  [FILE_TYPES.SURFACE_FLINGER_DUMP]: {
297    name: 'SurfaceFlinger dump',
298    decoder: protoDecoder,
299    decoderParams: {
300      type: FILE_TYPES.SURFACE_FLINGER_DUMP,
301      mime: 'application/octet-stream',
302      protoType: SfDumpMessage,
303      transform: (decoded) => transformLayers(true /* includesCompositionState*/, decoded),
304      timeline: false,
305    },
306  },
307  [FILE_TYPES.WINDOW_MANAGER_DUMP]: {
308    name: 'WindowManager dump',
309    decoder: protoDecoder,
310    decoderParams: {
311      type: FILE_TYPES.WINDOW_MANAGER_DUMP,
312      mime: 'application/octet-stream',
313      protoType: WmDumpMessage,
314      transform: WindowManagerDump.fromProto,
315      timeline: false,
316    },
317  },
318  [FILE_TYPES.WAYLAND_DUMP]: {
319    name: 'Wayland dump',
320    decoder: protoDecoder,
321    decoderParams: {
322      type: FILE_TYPES.WAYLAND_DUMP,
323      mime: 'application/octet-stream',
324      protoType: WaylandDumpMessage,
325      transform: transform_wl_outputstate,
326      timeline: false,
327    },
328  },
329  [FILE_TYPES.SCREEN_RECORDING]: {
330    name: 'Screen recording',
331    decoder: videoDecoder,
332    decoderParams: {
333      type: FILE_TYPES.SCREEN_RECORDING,
334      mime: 'video/mp4',
335      videoDecoder: mp4Decoder,
336    },
337  },
338  [FILE_TYPES.TRANSACTIONS_TRACE]: {
339    name: 'Transaction',
340    decoder: protoDecoder,
341    decoderParams: {
342      type: FILE_TYPES.TRANSACTIONS_TRACE,
343      mime: 'application/octet-stream',
344      protoType: SfTransactionTraceMessage,
345      transform: transform_transaction_trace,
346      timeline: true,
347    },
348  },
349  [FILE_TYPES.PROTO_LOG]: {
350    name: 'ProtoLog',
351    decoder: protoDecoder,
352    decoderParams: {
353      type: FILE_TYPES.PROTO_LOG,
354      mime: 'application/octet-stream',
355      protoType: ProtoLogMessage,
356      transform: transformProtolog,
357      timeline: true,
358    },
359  },
360  [FILE_TYPES.SYSTEM_UI]: {
361    name: 'SystemUI trace',
362    decoder: protoDecoder,
363    decoderParams: {
364      type: FILE_TYPES.SYSTEM_UI,
365      mime: 'application/octet-stream',
366      protoType: SystemUiTraceMessage,
367      transform: transform_sysui_trace,
368      timeline: true,
369    },
370  },
371  [FILE_TYPES.LAUNCHER]: {
372    name: 'Launcher trace',
373    decoder: protoDecoder,
374    decoderParams: {
375      type: FILE_TYPES.LAUNCHER,
376      mime: 'application/octet-stream',
377      protoType: LauncherTraceMessage,
378      transform: transform_launcher_trace,
379      timeline: true,
380    },
381  },
382  [FILE_TYPES.IME_TRACE_CLIENTS]: {
383    name: 'InputMethodClients trace',
384    decoder: protoDecoder,
385    decoderParams: {
386      type: FILE_TYPES.IME_TRACE_CLIENTS,
387      mime: 'application/octet-stream',
388      protoType: InputMethodClientsTraceMessage,
389      transform: transform_ime_trace_clients,
390      timeline: true,
391    },
392  },
393  [FILE_TYPES.IME_TRACE_SERVICE]: {
394    name: 'InputMethodService trace',
395    decoder: protoDecoder,
396    decoderParams: {
397      type: FILE_TYPES.IME_TRACE_SERVICE,
398      mime: 'application/octet-stream',
399      protoType: InputMethodServiceTraceMessage,
400      transform: transform_ime_trace_service,
401      timeline: true,
402    },
403  },
404  [FILE_TYPES.IME_TRACE_MANAGERSERVICE]: {
405    name: 'InputMethodManagerService trace',
406    decoder: protoDecoder,
407    decoderParams: {
408      type: FILE_TYPES.IME_TRACE_MANAGERSERVICE,
409      mime: 'application/octet-stream',
410      protoType: InputMethodManagerServiceTraceMessage,
411      transform: transform_ime_trace_managerservice,
412      timeline: true,
413    },
414  },
415};
416
417function lookup_type(protoPath, type) {
418  return protobuf.Root.fromJSON(protoPath).lookupType(type);
419}
420
421// Replace enum values with string representation and
422// add default values to the proto objects. This function also handles
423// a special case with TransformProtos where the matrix may be derived
424// from the transform type.
425function modifyProtoFields(protoObj, displayDefaults) {
426  if (!protoObj || protoObj !== Object(protoObj) || !protoObj.$type) {
427    return;
428  }
429
430  for (const fieldName in protoObj.$type.fields) {
431    if (protoObj.$type.fields.hasOwnProperty(fieldName)) {
432      const fieldProperties = protoObj.$type.fields[fieldName];
433      const field = protoObj[fieldName];
434
435      if (Array.isArray(field)) {
436        field.forEach((item, _) => {
437          modifyProtoFields(item, displayDefaults);
438        });
439        continue;
440      }
441
442      if (displayDefaults && !(field)) {
443        protoObj[fieldName] = fieldProperties.defaultValue;
444      }
445
446      if (fieldProperties.type === 'TransformProto') {
447        fill_transform_data(protoObj[fieldName]);
448        continue;
449      }
450
451      if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
452        protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
453        continue;
454      }
455      modifyProtoFields(protoObj[fieldName], displayDefaults);
456    }
457  }
458}
459
460function decodeAndTransformProto(buffer, params, displayDefaults) {
461  const decoded = params.protoType.decode(buffer);
462  modifyProtoFields(decoded, displayDefaults);
463  const transformed = params.transform(decoded);
464
465  return transformed;
466}
467
468function protoDecoder(buffer, params, fileName, store) {
469  const transformed = decodeAndTransformProto(buffer, params, store.displayDefaults);
470  let data;
471  if (params.timeline) {
472    data = transformed.entries ?? transformed.children;
473  } else {
474    data = [transformed];
475  }
476  const blobUrl = URL.createObjectURL(new Blob([buffer], {type: params.mime}));
477  return dataFile(fileName, data.map((x) => x.timestamp), data, blobUrl, params.type);
478}
479
480function videoDecoder(buffer, params, fileName, store) {
481  const [data, timeline] = params.videoDecoder(buffer);
482  const blobUrl = URL.createObjectURL(new Blob([data], {type: params.mime}));
483  return dataFile(fileName, timeline, blobUrl, blobUrl, params.type);
484}
485
486function dataFile(filename, timeline, data, blobUrl, type) {
487  return {
488    filename: filename,
489    // Object is frozen for performance reasons
490    // It will prevent Vue from making it a reactive object which will be very slow as the timeline gets larger.
491    timeline: Object.freeze(timeline),
492    data: data,
493    blobUrl: blobUrl,
494    type: type,
495    selectedIndex: 0,
496    destroy() {
497      URL.revokeObjectURL(this.blobUrl);
498    },
499  };
500}
501
502function arrayEquals(a, b) {
503  if (a.length !== b.length) {
504    return false;
505  }
506  for (let i = 0; i < a.length; i++) {
507    if (a[i] != b[i]) {
508      return false;
509    }
510  }
511  return true;
512}
513
514function arrayStartsWith(array, prefix) {
515  return arrayEquals(array.slice(0, prefix.length), prefix);
516}
517
518function decodedFile(fileType, buffer, fileName, store) {
519  const fileDecoder = FILE_DECODERS[fileType];
520  return [fileType, fileDecoder.decoder(buffer, fileDecoder.decoderParams, fileName, store)];
521}
522
523function detectAndDecode(buffer, fileName, store) {
524  if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) {
525    return decodedFile(FILE_TYPES.SURFACE_FLINGER_TRACE, buffer, fileName, store);
526  }
527  if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) {
528    return decodedFile(FILE_TYPES.WINDOW_MANAGER_TRACE, buffer, fileName, store);
529  }
530  if (arrayStartsWith(buffer, MPEG4_MAGIC_NMBER)) {
531    return decodedFile(FILE_TYPES.SCREEN_RECORDING, buffer, fileName, store);
532  }
533  if (arrayStartsWith(buffer, WAYLAND_TRACE_MAGIC_NUMBER)) {
534    return decodedFile(FILE_TYPES.WAYLAND_TRACE, buffer, fileName, store);
535  }
536  if (arrayStartsWith(buffer, PROTO_LOG_MAGIC_NUMBER)) {
537    return decodedFile(FILE_TYPES.PROTO_LOG, buffer, fileName, store);
538  }
539  if (arrayStartsWith(buffer, SYSTEM_UI_MAGIC_NUMBER)) {
540    return decodedFile(FILE_TYPES.SYSTEM_UI, buffer, fileName, store);
541  }
542  if (arrayStartsWith(buffer, LAUNCHER_MAGIC_NUMBER)) {
543    return decodedFile(FILE_TYPES.LAUNCHER, buffer, fileName, store);
544  }
545  if (arrayStartsWith(buffer, IMC_TRACE_MAGIC_NUMBER)) {
546    return decodedFile(FILE_TYPES.IME_TRACE_CLIENTS, buffer, fileName, store);
547  }
548  if (arrayStartsWith(buffer, IMS_TRACE_MAGIC_NUMBER)) {
549    return decodedFile(FILE_TYPES.IME_TRACE_SERVICE, buffer, fileName, store);
550  }
551  if (arrayStartsWith(buffer, IMM_TRACE_MAGIC_NUMBER)) {
552    return decodedFile(FILE_TYPES.IME_TRACE_MANAGERSERVICE, buffer, fileName, store);
553  }
554
555  // TODO(b/169305853): Add magic number at beginning of file for better auto detection
556  for (const [filetype, condition] of [
557    [FILE_TYPES.TRANSACTIONS_TRACE, (file) => file.data.length > 0],
558    [FILE_TYPES.WAYLAND_DUMP, (file) => (file.data.length > 0 && file.data.children[0] > 0) || file.data.length > 1],
559    [FILE_TYPES.WINDOW_MANAGER_DUMP],
560    [FILE_TYPES.SURFACE_FLINGER_DUMP],
561  ]) {
562    try {
563      const [, fileData] = decodedFile(filetype, buffer, fileName, store);
564
565      // A generic file will often wrongly be decoded as an empty wayland dump file
566      if (condition && !condition(fileData)) {
567        // Fall through to next filetype
568        continue;
569      }
570
571      return [filetype, fileData];
572    } catch (ex) {
573      // ignore exception and fall through to next filetype
574    }
575  }
576  throw new UndetectableFileType('Unable to detect file');
577}
578
579/**
580 * Error is raised when detectAndDecode is called but the file can't be
581 * automatically detected as being of a compatible file type.
582 */
583class UndetectableFileType extends Error { }
584
585export {detectAndDecode, decodeAndTransformProto, FILE_TYPES, TRACE_INFO, TRACE_TYPES, DUMP_TYPES, DUMP_INFO, FILE_DECODERS, FILE_ICONS, UndetectableFileType};
586