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
18import jsonProtoDefsWm from 'frameworks/base/core/proto/android/server/windowmanagertrace.proto'
19import jsonProtoDefsProtoLog from 'frameworks/base/core/proto/android/server/protolog.proto'
20import jsonProtoDefsSf from 'frameworks/native/services/surfaceflinger/layerproto/layerstrace.proto'
21import jsonProtoDefsTransaction from 'frameworks/native/cmds/surfacereplayer/proto/src/trace.proto'
22import jsonProtoDefsWl from 'WaylandSafePath/waylandtrace.proto'
23import jsonProtoDefsSysUi from 'frameworks/base/packages/SystemUI/src/com/android/systemui/tracing/sysui_trace.proto'
24import jsonProtoDefsLauncher from 'packages/apps/Launcher3/protos/launcher_trace_file.proto'
25import protobuf from 'protobufjs'
26import { transform_layers, transform_layers_trace } from './transform_sf.js'
27import { transform_window_service, transform_window_trace } from './transform_wm.js'
28import { transform_transaction_trace } from './transform_transaction.js'
29import { transform_wl_outputstate, transform_wayland_trace } from './transform_wl.js'
30import { transform_protolog } from './transform_protolog.js'
31import { transform_sysui_trace } from './transform_sys_ui.js'
32import { transform_launcher_trace } from './transform_launcher.js'
33import { fill_transform_data } from './matrix_utils.js'
34import { mp4Decoder } from './decodeVideo.js'
35
36var WmTraceMessage = lookup_type(jsonProtoDefsWm, "com.android.server.wm.WindowManagerTraceFileProto");
37var WmDumpMessage = lookup_type(jsonProtoDefsWm, "com.android.server.wm.WindowManagerServiceDumpProto");
38var SfTraceMessage = lookup_type(jsonProtoDefsSf, "android.surfaceflinger.LayersTraceFileProto");
39var SfDumpMessage = lookup_type(jsonProtoDefsSf, "android.surfaceflinger.LayersProto");
40var SfTransactionTraceMessage = lookup_type(jsonProtoDefsTransaction, "Trace");
41var WaylandTraceMessage = lookup_type(jsonProtoDefsWl, "org.chromium.arc.wayland_composer.TraceFileProto");
42var WaylandDumpMessage = lookup_type(jsonProtoDefsWl, "org.chromium.arc.wayland_composer.OutputStateProto");
43var ProtoLogMessage = lookup_type(jsonProtoDefsProtoLog, "com.android.server.protolog.ProtoLogFileProto");
44var SystemUiTraceMessage = lookup_type(jsonProtoDefsSysUi, "com.android.systemui.tracing.SystemUiTraceFileProto");
45var LauncherTraceMessage = lookup_type(jsonProtoDefsLauncher, "com.android.launcher3.tracing.LauncherTraceFileProto");
46
47const LAYER_TRACE_MAGIC_NUMBER = [0x09, 0x4c, 0x59, 0x52, 0x54, 0x52, 0x41, 0x43, 0x45] // .LYRTRACE
48const WINDOW_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45] // .WINTRACE
49const MPEG4_MAGIC_NMBER = [0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6d, 0x70, 0x34, 0x32] // ....ftypmp42
50const WAYLAND_TRACE_MAGIC_NUMBER = [0x09, 0x57, 0x59, 0x4c, 0x54, 0x52, 0x41, 0x43, 0x45] // .WYLTRACE
51const PROTO_LOG_MAGIC_NUMBER = [0x09, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47] // .PROTOLOG
52const SYSTEM_UI_MAGIC_NUMBER = [0x09, 0x53, 0x59, 0x53, 0x55, 0x49, 0x54, 0x52, 0x43] // .SYSUITRC
53const LAUNCHER_MAGIC_NUMBER = [0x09, 0x4C, 0x4E, 0x43, 0x48, 0x52, 0x54, 0x52, 0x43] // .LNCHRTRC
54
55const DATA_TYPES = {
56  WINDOW_MANAGER: {
57    name: "WindowManager",
58    icon: "view_compact",
59    mime: "application/octet-stream",
60  },
61  SURFACE_FLINGER: {
62    name: "SurfaceFlinger",
63    icon: "filter_none",
64    mime: "application/octet-stream",
65  },
66  SCREEN_RECORDING: {
67    name: "Screen recording",
68    icon: "videocam",
69    mime: "video/mp4",
70  },
71  TRANSACTION: {
72    name: "Transaction",
73    icon: "timeline",
74    mime: "application/octet-stream",
75  },
76  WAYLAND: {
77    name: "Wayland",
78    icon: "filter_none",
79    mime: "application/octet-stream",
80  },
81  PROTO_LOG: {
82    name: "ProtoLog",
83    icon: "notes",
84    mime: "application/octet-stream",
85  },
86  SYSTEM_UI: {
87    name: "SystemUI",
88    icon: "filter_none",
89    mime: "application/octet-stream",
90  },
91  LAUNCHER: {
92    name: "Launcher",
93    icon: "filter_none",
94    mime: "application/octet-stream",
95  },
96}
97
98const FILE_TYPES = {
99  'window_trace': {
100    name: "WindowManager trace",
101    dataType: DATA_TYPES.WINDOW_MANAGER,
102    decoder: protoDecoder,
103    decoderParams: {
104      protoType: WmTraceMessage,
105      transform: transform_window_trace,
106      timeline: true,
107    },
108  },
109  'layers_trace': {
110    name: "SurfaceFlinger trace",
111    dataType: DATA_TYPES.SURFACE_FLINGER,
112    decoder: protoDecoder,
113    decoderParams: {
114      protoType: SfTraceMessage,
115      transform: transform_layers_trace,
116      timeline: true,
117    },
118  },
119  'wl_trace': {
120    name: "Wayland trace",
121    dataType: DATA_TYPES.WAYLAND,
122    decoder: protoDecoder,
123    decoderParams: {
124      protoType: WaylandTraceMessage,
125      transform: transform_wayland_trace,
126      timeline: true,
127    },
128  },
129  'layers_dump': {
130    name: "SurfaceFlinger dump",
131    dataType: DATA_TYPES.SURFACE_FLINGER,
132    decoder: protoDecoder,
133    decoderParams: {
134      protoType: SfDumpMessage,
135      transform: (decoded) => transform_layers(true /*includesCompositionState*/, decoded),
136      timeline: false,
137    },
138  },
139  'window_dump': {
140    name: "WindowManager dump",
141    dataType: DATA_TYPES.WINDOW_MANAGER,
142    decoder: protoDecoder,
143    decoderParams: {
144      protoType: WmDumpMessage,
145      transform: transform_window_service,
146      timeline: false,
147    },
148  },
149  'wl_dump': {
150    name: "Wayland dump",
151    dataType: DATA_TYPES.WAYLAND,
152    decoder: protoDecoder,
153    decoderParams: {
154      protoType: WaylandDumpMessage,
155      transform: transform_wl_outputstate,
156      timeline: false,
157    },
158  },
159  'screen_recording': {
160    name: "Screen recording",
161    dataType: DATA_TYPES.SCREEN_RECORDING,
162    decoder: videoDecoder,
163    decoderParams: {
164      videoDecoder: mp4Decoder,
165    },
166  },
167  'transaction': {
168    name: "Transaction",
169    dataType: DATA_TYPES.TRANSACTION,
170    decoder: protoDecoder,
171    decoderParams: {
172      protoType: SfTransactionTraceMessage,
173      transform: transform_transaction_trace,
174      timeline: true,
175    }
176  },
177  'proto_log': {
178    name: "ProtoLog",
179    dataType: DATA_TYPES.PROTO_LOG,
180    decoder: protoDecoder,
181    decoderParams: {
182      protoType: ProtoLogMessage,
183      transform: transform_protolog,
184      timeline: true,
185    }
186  },
187  'system_ui_trace': {
188    name: "SystemUI trace",
189    dataType: DATA_TYPES.SYSTEM_UI,
190    decoder: protoDecoder,
191    decoderParams: {
192      protoType: SystemUiTraceMessage,
193      transform: transform_sysui_trace,
194      timeline: true,
195    }
196  },
197  'launcher_trace': {
198    name: "Launcher trace",
199    dataType: DATA_TYPES.LAUNCHER,
200    decoder: protoDecoder,
201    decoderParams: {
202      protoType: LauncherTraceMessage,
203      transform: transform_launcher_trace,
204      timeline: true,
205    }
206  },
207};
208
209function lookup_type(protoPath, type) {
210  return protobuf.Root.fromJSON(protoPath).lookupType(type);
211}
212
213// Replace enum values with string representation and
214// add default values to the proto objects. This function also handles
215// a special case with TransformProtos where the matrix may be derived
216// from the transform type.
217function modifyProtoFields(protoObj, displayDefaults) {
218  if (!protoObj || protoObj !== Object(protoObj) || !protoObj.$type) {
219    return;
220  }
221  for (var fieldName in protoObj.$type.fields) {
222    var fieldProperties = protoObj.$type.fields[fieldName];
223    var field = protoObj[fieldName];
224
225    if (Array.isArray(field)) {
226      field.forEach((item, _) => {
227        modifyProtoFields(item, displayDefaults);
228      })
229      continue;
230    }
231
232    if (displayDefaults && !(field)) {
233      protoObj[fieldName] = fieldProperties.defaultValue;
234    }
235
236    if (fieldProperties.type === 'TransformProto') {
237      fill_transform_data(protoObj[fieldName]);
238      continue;
239    }
240
241    if (fieldProperties.resolvedType && fieldProperties.resolvedType.valuesById) {
242      protoObj[fieldName] = fieldProperties.resolvedType.valuesById[protoObj[fieldProperties.name]];
243      continue;
244    }
245    modifyProtoFields(protoObj[fieldName], displayDefaults);
246  }
247}
248
249function protoDecoder(buffer, fileType, fileName, store) {
250  var decoded = fileType.decoderParams.protoType.decode(buffer);
251  modifyProtoFields(decoded, store.displayDefaults);
252  var transformed = fileType.decoderParams.transform(decoded);
253  var data
254  if (fileType.decoderParams.timeline) {
255    data = transformed.children;
256  } else {
257    data = [transformed];
258  }
259  let blobUrl = URL.createObjectURL(new Blob([buffer], { type: fileType.dataType.mime }));
260  return dataFile(fileName, data.map(x => x.timestamp), data, blobUrl, fileType.dataType);
261}
262
263function videoDecoder(buffer, fileType, fileName, store) {
264  let [data, timeline] = fileType.decoderParams.videoDecoder(buffer);
265  let blobUrl = URL.createObjectURL(new Blob([data], { type: fileType.dataType.mime }));
266  return dataFile(fileName, timeline, blobUrl, blobUrl, fileType.dataType);
267}
268
269function dataFile(filename, timeline, data, blobUrl, type) {
270  return {
271    filename: filename,
272    timeline: timeline,
273    data: data,
274    blobUrl: blobUrl,
275    type: type,
276    selectedIndex: 0,
277    destroy() {
278      URL.revokeObjectURL(this.blobUrl);
279    },
280  }
281}
282
283function arrayEquals(a, b) {
284  if (a.length !== b.length) {
285    return false;
286  }
287  for (var i = 0; i < a.length; i++) {
288    if (a[i] != b[i]) {
289      return false;
290    }
291  }
292  return true;
293}
294
295function arrayStartsWith(array, prefix) {
296  return arrayEquals(array.slice(0, prefix.length), prefix);
297}
298
299function decodedFile(fileType, buffer, fileName, store) {
300  return [fileType, fileType.decoder(buffer, fileType, fileName, store)];
301}
302
303function detectAndDecode(buffer, fileName, store) {
304  if (arrayStartsWith(buffer, LAYER_TRACE_MAGIC_NUMBER)) {
305    return decodedFile(FILE_TYPES['layers_trace'], buffer, fileName, store);
306  }
307  if (arrayStartsWith(buffer, WINDOW_TRACE_MAGIC_NUMBER)) {
308    return decodedFile(FILE_TYPES['window_trace'], buffer, fileName, store);
309  }
310  if (arrayStartsWith(buffer, MPEG4_MAGIC_NMBER)) {
311    return decodedFile(FILE_TYPES['screen_recording'], buffer, fileName, store);
312  }
313  if (arrayStartsWith(buffer, WAYLAND_TRACE_MAGIC_NUMBER)) {
314    return decodedFile(FILE_TYPES['wl_trace'], buffer, fileName, store);
315  }
316  if (arrayStartsWith(buffer, PROTO_LOG_MAGIC_NUMBER)) {
317    return decodedFile(FILE_TYPES['proto_log'], buffer, fileName, store);
318  }
319  if (arrayStartsWith(buffer, SYSTEM_UI_MAGIC_NUMBER)) {
320    return decodedFile(FILE_TYPES['system_ui_trace'], buffer, fileName, store);
321  }
322  if (arrayStartsWith(buffer, LAUNCHER_MAGIC_NUMBER)) {
323    return decodedFile(FILE_TYPES['launcher_trace'], buffer, fileName, store);
324  }
325  for (var name of ['transaction', 'layers_dump', 'window_dump', 'wl_dump']) {
326    try {
327      return decodedFile(FILE_TYPES[name], buffer, fileName, store);
328    } catch (ex) {
329      // ignore exception and try next filetype
330    }
331  }
332  throw new Error('Unable to detect file');
333}
334
335export { detectAndDecode, DATA_TYPES, FILE_TYPES };
336