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 {HierarchyTreeBuilderInputMethod} from 'parsers/input_method/hierarchy_tree_builder_input_method';
18import {AddDefaults} from 'parsers/operations/add_defaults';
19import {SetFormatters} from 'parsers/operations/set_formatters';
20import {TranslateIntDef} from 'parsers/operations/translate_intdef';
21import {TamperedProtoField} from 'parsers/tampered_message_type';
22import {perfetto} from 'protos/ime/latest/static';
23import {android} from 'protos/ime/udc/static';
24import {HierarchyTreeNode} from 'trace/tree_node/hierarchy_tree_node';
25import {LazyPropertiesStrategyType} from 'trace/tree_node/properties_provider';
26import {PropertiesProviderBuilder} from 'trace/tree_node/properties_provider_builder';
27import {PropertyTreeBuilderFromProto} from 'trace/tree_node/property_tree_builder_from_proto';
28import {PropertyTreeNode} from 'trace/tree_node/property_tree_node';
29
30export class HierarchyTreeClientsFactory {
31  private static readonly ENTRY_DENYLIST_PROPERTIES = ['client'];
32  private static readonly ENTRY_EAGER_PROPERTIES = ['where'];
33  private static readonly CLIENT_EAGER_PROPERTIES = [
34    'viewRootImpl',
35    'inputMethodManager',
36    'editorInfo',
37  ];
38
39  private SetFormattersClient: SetFormatters;
40  private TranslateIntDefClient: TranslateIntDef;
41  private AddDefaultsClientEager: AddDefaults;
42  private AddDefaultsClientLazy: AddDefaults;
43  private SetFormattersEntry: SetFormatters;
44  private AddDefaultsEntryEager: AddDefaults;
45  private AddDefaultsEntryLazy: AddDefaults;
46
47  constructor(entryField: TamperedProtoField, clientField: TamperedProtoField) {
48    this.SetFormattersClient = new SetFormatters(clientField);
49    this.TranslateIntDefClient = new TranslateIntDef(clientField);
50    this.AddDefaultsClientEager = new AddDefaults(
51      clientField,
52      HierarchyTreeClientsFactory.CLIENT_EAGER_PROPERTIES,
53    );
54    this.AddDefaultsClientLazy = new AddDefaults(
55      clientField,
56      undefined,
57      HierarchyTreeClientsFactory.CLIENT_EAGER_PROPERTIES,
58    );
59    this.SetFormattersEntry = new SetFormatters(entryField);
60    this.AddDefaultsEntryEager = new AddDefaults(
61      entryField,
62      HierarchyTreeClientsFactory.ENTRY_EAGER_PROPERTIES,
63    );
64    this.AddDefaultsEntryLazy = new AddDefaults(
65      entryField,
66      undefined,
67      HierarchyTreeClientsFactory.ENTRY_EAGER_PROPERTIES.concat(
68        HierarchyTreeClientsFactory.ENTRY_DENYLIST_PROPERTIES,
69      ),
70    );
71  }
72
73  makeHierarchyTree(
74    entryProto:
75      | android.view.inputmethod.IInputMethodClientsTraceProto
76      | perfetto.protos.IInputMethodClientsTraceProto,
77  ): HierarchyTreeNode {
78    const entry = new PropertiesProviderBuilder()
79      .setEagerProperties(this.makeEntryEagerPropertiesTree(entryProto))
80      .setLazyPropertiesStrategy(
81        this.makeEntryLazyPropertiesStrategy(entryProto),
82      )
83      .setEagerOperations([this.AddDefaultsEntryEager])
84      .setCommonOperations([this.SetFormattersEntry])
85      .setLazyOperations([this.AddDefaultsEntryLazy])
86      .build();
87
88    const client = new PropertiesProviderBuilder()
89      .setEagerProperties(this.makeClientEagerPropertiesTree(entryProto.client))
90      .setLazyPropertiesStrategy(
91        this.makeClientLazyPropertiesStrategy(entryProto.client),
92      )
93      .setEagerOperations(
94        entryProto.client ? [this.AddDefaultsClientEager] : [],
95      )
96      .setCommonOperations([
97        this.SetFormattersClient,
98        this.TranslateIntDefClient,
99      ])
100      .setLazyOperations(entryProto.client ? [this.AddDefaultsClientLazy] : [])
101      .build();
102
103    return new HierarchyTreeBuilderInputMethod()
104      .setRoot(entry)
105      .setChildren([client])
106      .build();
107  }
108
109  private makeEntryEagerPropertiesTree(
110    entryProto:
111      | android.view.inputmethod.IInputMethodClientsTraceProto
112      | perfetto.protos.IInputMethodClientsTraceProto,
113  ): PropertyTreeNode {
114    const denyList: string[] = [];
115    Object.getOwnPropertyNames(entryProto).forEach((it) => {
116      if (!HierarchyTreeClientsFactory.ENTRY_EAGER_PROPERTIES.includes(it)) {
117        denyList.push(it);
118      }
119    });
120
121    return new PropertyTreeBuilderFromProto()
122      .setData(entryProto)
123      .setRootId('InputMethodClients')
124      .setRootName('entry')
125      .setDenyList(denyList)
126      .build();
127  }
128
129  private makeEntryLazyPropertiesStrategy(
130    entryProto:
131      | android.view.inputmethod.IInputMethodClientsTraceProto
132      | perfetto.protos.IInputMethodClientsTraceProto,
133  ): LazyPropertiesStrategyType {
134    return async () => {
135      return new PropertyTreeBuilderFromProto()
136        .setData(entryProto)
137        .setRootId('InputMethodClients')
138        .setRootName('entry')
139        .setDenyList(
140          HierarchyTreeClientsFactory.ENTRY_EAGER_PROPERTIES.concat(
141            HierarchyTreeClientsFactory.ENTRY_DENYLIST_PROPERTIES,
142          ),
143        )
144        .build();
145    };
146  }
147
148  private makeClientEagerPropertiesTree(
149    clientProto:
150      | android.view.inputmethod.InputMethodClientsTraceProto.IClientSideProto
151      | perfetto.protos.InputMethodClientsTraceProto.IClientSideProto
152      | null
153      | undefined,
154  ): PropertyTreeNode {
155    const denyList: string[] = [];
156    let data: any = clientProto;
157    if (clientProto) {
158      Object.getOwnPropertyNames(clientProto).forEach((it) => {
159        if (!HierarchyTreeClientsFactory.CLIENT_EAGER_PROPERTIES.includes(it)) {
160          denyList.push(it);
161        }
162      });
163    } else {
164      data = {client: null};
165    }
166
167    return new PropertyTreeBuilderFromProto()
168      .setData(data)
169      .setRootId('InputMethodClients')
170      .setRootName('client')
171      .setDenyList(denyList)
172      .setVisitPrototype(false)
173      .build();
174  }
175
176  private makeClientLazyPropertiesStrategy(
177    clientProto:
178      | android.view.inputmethod.InputMethodClientsTraceProto.IClientSideProto
179      | perfetto.protos.InputMethodClientsTraceProto.IClientSideProto
180      | null
181      | undefined,
182  ): LazyPropertiesStrategyType {
183    return async () => {
184      return new PropertyTreeBuilderFromProto()
185        .setData(clientProto ?? {client: null})
186        .setRootId('InputMethodClients')
187        .setRootName('client')
188        .setDenyList(HierarchyTreeClientsFactory.CLIENT_EAGER_PROPERTIES)
189        .setVisitPrototype(false)
190        .build();
191    };
192  }
193}
194