1/*
2 * Copyright (C) 2024 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
17import {assertDefined} from 'common/assert_utils';
18import {ParserTimestampConverter} from 'common/timestamp_converter';
19import {SetFormatters} from 'parsers/operations/set_formatters';
20import {TranslateIntDef} from 'parsers/operations/translate_intdef';
21import {AbstractParser} from 'parsers/perfetto/abstract_parser';
22import {FakeProtoBuilder} from 'parsers/perfetto/fake_proto_builder';
23import {FakeProtoTransformer} from 'parsers/perfetto/fake_proto_transformer';
24import {TamperedMessageType} from 'parsers/tampered_message_type';
25import root from 'protos/input/latest/json';
26import {perfetto} from 'protos/input/latest/static';
27import {TraceFile} from 'trace/trace_file';
28import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
29import {WasmEngineProxy} from 'trace_processor/wasm_engine_proxy';
30
31export abstract class AbstractInputEventParser extends AbstractParser<PropertyTreeNode> {
32  protected static readonly WrapperProto = TamperedMessageType.tamper(
33    root.lookupType('perfetto.protos.InputEventWrapper'),
34  );
35
36  private static readonly DispatchEventsField =
37    AbstractInputEventParser.WrapperProto.fields['windowDispatchEvents'];
38
39  private static readonly DISPATCH_EVENT_OPS = [
40    new SetFormatters(AbstractInputEventParser.DispatchEventsField),
41    new TranslateIntDef(AbstractInputEventParser.DispatchEventsField),
42  ];
43
44  private static readonly DispatchTableName = 'android_input_event_dispatch';
45
46  private dispatchEventTransformer: FakeProtoTransformer;
47
48  protected constructor(
49    traceFile: TraceFile,
50    traceProcessor: WasmEngineProxy,
51    timestampConverter: ParserTimestampConverter,
52  ) {
53    super(traceFile, traceProcessor, timestampConverter);
54
55    this.dispatchEventTransformer = new FakeProtoTransformer(
56      assertDefined(
57        AbstractInputEventParser.DispatchEventsField.tamperedMessageType,
58      ),
59    );
60  }
61
62  protected async getDispatchEvents(
63    eventId: number,
64  ): Promise<perfetto.protos.AndroidWindowInputDispatchEvent[]> {
65    const sql = `
66        SELECT d.id,
67               args.key,
68               args.value_type,
69               args.int_value,
70               args.string_value,
71               args.real_value
72        FROM ${AbstractInputEventParser.DispatchTableName} AS d
73                 INNER JOIN args ON d.arg_set_id = args.arg_set_id
74        WHERE d.event_id = ${eventId}
75        ORDER BY d.id;
76    `;
77    const result = await this.traceProcessor.query(sql).waitAllRows();
78
79    const dispatchEvents: perfetto.protos.AndroidWindowInputDispatchEvent[] =
80      [];
81    for (const it = result.iter({}); it.valid(); ) {
82      const builder = new FakeProtoBuilder();
83      const prevId = it.get('id');
84      while (it.valid() && it.get('id') === prevId) {
85        builder.addArg(
86          it.get('key') as string,
87          it.get('value_type') as string,
88          it.get('int_value') as bigint | undefined,
89          it.get('real_value') as number | undefined,
90          it.get('string_value') as string | undefined,
91        );
92        it.next();
93      }
94      dispatchEvents.push(builder.build());
95    }
96    return dispatchEvents;
97  }
98
99  protected override getStdLibModuleName(): string | undefined {
100    return 'android.input';
101  }
102
103  protected processDispatchEventsTree(tree: PropertyTreeNode) {
104    AbstractInputEventParser.DISPATCH_EVENT_OPS.forEach((operation) => {
105      operation.apply(tree);
106    });
107  }
108}
109